Cymen Vig

Software Craftsman

When to use browserify-rails and when not to

In 2014, I joined a startup and began rewriting their single page web application. One developer had been working on that particular part of the site and it was long in tooth. Startups need to iterate fast and try new ideas and their codebases typically reflect that. Many developers at startups talk about switching from one language to another as if that was the solution to their problems but in reality, most of the time, the rewrite is simply getting rid of all the cruft that has built up along with the knowledge the developers have built by working on the existing code.

So it was no surprise that the JavaScript didn’t use modules, had a lot of very confusing global usage and a fair amount of dead code. Because the rest of the site had been rewritten from PHP to Ruby on Rails, I wanted to use tooling that the average Rails developer would be comfortable with. But I also wanted CommonJS modules — my experience has been that the simple things like modules are critical.

In the Rails world, the tooling typically hooks into the request cycle. A developer can change a Ruby file, reload the page and their change is live. It would have been possible to use browserify directly by spinning up a process (say via foreman which was already in use on the project) however the behavior with that approach would not be the same. The rebuilding of the JavaScript would not be synchronous with the page request.

Another great developer on the team was already using browserify and I was excited at the prospect of using node modules. I found the hsume2-browserify-rails Ruby gem. It was missing some important features so I forked it and we began using it to refactor our existing JavaScript to CommonJS. I later was able to get my merges in along with getting the browerify-rails gem name from another developer that was not using it.

Because it was a painful process that would be easy to get bogged down in, we decided to rewrite it as is — to avoid spending too much time refactoring the existing code. That way we would work in phases: get the code base to modules then focus on refactoring the individual modules (while of course juggling add new features).

In the end, I contribute quite a bit to the gem and continue to work on it today. But I have noticed that not everyone is sure when using something like browserify-rails is appropriate. Of course, I think it is best that people make up their own minds but I will outline my thinking.

On an existing code base that already has a bunch of JavaScript that is typical non-modular code, using browerify-rails makes complete sense. You can rewrite to modules in parts as you go — just change the code to a module and have some other code require it and export it as a global again. That will let you refactor to modules without a huge amount of interruption and it works as one might expect in the Rails ecosystem.

On a fresh project that is expected to grow and have a large amount of client-side rendering JavaScript, I would not use browserify-rails. I would instead use webpack or browserify directly. This of course disrupts the synchronous build process but I argue that it is less important because if the rendering is all happening client-side, the asynchronous disconnect will not be a problem the vast majority of the time. There are also performance benefits to using browserify or webpack directly instead of through the Rails asset pipeline and it keeps things simpler.

Of course, that begs the question: is Ruby on Rails an appropriate stack for a modern client-side rending web application. That is a very loaded question but I do think it is something that should be asked on any new project. Isomorphic web applications are coming and they are coming for solid technical reasons. Choose your stack carefully.

Comments and Reactions

My experience with Hired

A couple of months ago I decided to try Hired.com. I was interviewing in San Francisco but it was very hit and miss. It was both hard to get time off to interview and sometimes those doing the interviewing were clearly unprepared. It felt like a huge waste of time.

I had some doubts about Hired.com. I’m starting a family and my career is very important to me. Could I trust that my interests would be represented? What pushed me over the edge was realizing my network in San Francisco was lacking. I realized that Hired.com was just like online dating — it could connect me to people that I might not meet.

So I signed up. I entered my profile and saw a message I would be in the next hiring round in a couple of days but could contact them to opt out. I was slightly uncomfortable but optimistic. One of the fields on the profile is your asking salary. I had already been thinking about my value on the market here — I chose to focus on a very specific area that is in high demand (although I think all areas of software development are in high demand in the SF Bay Area). So I entered what I thought I was worth to a project that could make full use of my skill set. I later learned this was on the high end of what Hired.com saw for developer positions.

The Monday my profile went live I was contacted by a number of companies. Each offer to interview came with compensation details. They were all from founders and outlined what their startup focused on. All contact was through the Hired.com web application which overall functioned as expected.

I waited a couple of days for more of the offers to come in. I rejected some at the low end and those that I had no interest in working on. One can choose to provide details if one wishes but again it was through the platform. Then I contacted 3–4 and went on with the hiring process. In total, 11 different startups contacted me.

The typical process was a phone conversation with one of the founders. If that went well and there was interest from both sides, we went on to a remote technical screen or an onsite interview. It was a bit overwhelming — going from interviewing maybe once every two weeks to having to juggle 3–4 potential interviews within a week or so was overwhelming. But it was also wonderful to feel that my interests and the interests of my potential employers were more in balance than the typical interview process.

What surprised me the most was that I had a number of wonderful conversations about software developer versus a more product-focused developer with one founder. It helped to clarify my thoughts and it was great to get validation that the way I liked to work was in demand.

I ended up going onsite to four startups. Each had compelling work and great teams. Of the four, one wasn’t a good match so I was left deciding between three. The last startup I interviewed with ended up having what I thought was the best fit for me. I discussed it with my wife and had a weekend to think about it and then accepted the position after some minor negotiation on equity.

I can’t recommend Hired.com enough. There is so much going on in San Francisco that nobody is going to have a perfect social network. There was no pressure to commit to only going with Hired.com opportunities. There was an awesome person at Hired.com who walked me through the process and was there to answer any question I had. I felt that I had someone on my side and it really helped to talk to another person who knew exactly what I was going through. I ended up with great job at a company I hadn’t heard of and I wouldn’t be where I am now without Hired.com.

I am not comfortable revealing my full compensation (see #TalkPay) but I will say that I ended up getting exactly what I asked for. Now it is on me to deliver that value to the startup I joined and I am extremely happy to do it knowing that I am being fairly compensated.

Full disclosure: the Hired.com links above are using my referral code. I’ll get a bonus if you use my link. A former coworker referred me to Hired.com and I was happy to send a bonus his way.

Comments and Reactions

Brother HL-2270DW and DD-WRT

The Brother HL-2270DW is an excellent and inexpensive laser printer with support for both wireless network connection and duplex printing. DD-WRT is a useful firmware replacement for many network devices. At first, the two did not want to play together but I figured out that the network settings for DW-WRT need to be:

  • Security Mode: WPA2 Personal
  • WPA Algorithms: AES

The key is the second option – if it is set to TKIP or TKIP+AES, the Brother printer cannot successfully connect via wireless.

Comments and Reactions

Debricking the Netgear WNR3500L

A recent attempt to flash a new firmware to my Netgear WNR3500L resulted in the top power light staying orange. At first, it seemed to take a tftp attempt to 192.168.1.1 on boot up with the special boot up sequence however I could not get it to work.

After reading online, I purchased a USB-TTL adapter on eBay and waited for it to arrive. Following the unbricking guide, I could see the router boot up via the serial port (using the USB-TTL adapter). I was able to enter the CFE console (I found it easier to press and hold down CTRL-C and then power up the device as PuTTY kept sending the key sequence). However the Ethernet portion of the router never came online so while I could ask it to accept a tftp attempt by starting tftpd, it was of no use.

I almost gave up, however some searching found a report of the same problem and the suggestion to try nvram erase. I ran the command and after the prompt came back, cycled the power to the device. On boot, I was again able to enter the CFE console but this time the Ethernet portion of the router came up and a quick tftp of the stock firmware and I was back online.

Comments and Reactions

Modular JavaScript

JavaScript is a complicated language. One of the difficult areas is scope. We can think of scope as the world a particular line of code can see. In JavaScript, if we neglect to use the var keyword for a variable, that variable is seen by the whole world from any line of code. Some large JavaScript applications intentionally use global variables to organize the system. Alternatively, a better pattern has emerged in which modules are used to access the other parts of the system. At first glance, this appears to be similar to an import in Java, a #include in C/C++, or a require in Ruby. But there is a very important difference: in most modular JavaScript approaches, the module is a scope that can hold state which is shared between all consumers of that module.

Almost a year ago, I started working on a huge JavaScript web application. At one point, it surpassed 600,000 lines of JavaScript not including third party libraries. The application used a framework that allowed for the creation of controllers and views. When a controller was defined, it was attached to the global scope by, in effect, omitting the var keyword. If this controller needed to be used by other JavaScript code, one could simply use the global variable to access it. The use of globals tightly coupled the system together in ways which made it hard to write new code using test-driven development. The tests would need to mock out all of the globals accessed by the code under test. After working with the system for a couple of months, it was apparent to us that this two year old application felt like it had four to five years of technical debt.

The good news was that we were not the only ones who felt this way. While the framework was firmly entrenched in the application, the team began to chip away at the globals and the whole approach that encouraged creation of these globals. The first step was adding asynchronous module definition to the frameworks dependency management function with define and require functions. We took this old school code:

thieve('subject_controller.js', function() {
  ...
});

And converted it to:

define(['subject_controller.js'], function(SubjectController) {
  ...
});

Here SubjectController is defined by subject_controller.js. This makes it trivial to define SubjectController as a mock. However one of the problems is that now the mock has replaced the SubjectController for the entirety of the scope. Having to put all the tests that depend on the mock in one file and the other tests in a different file seemed like a serious limitation, particularly when one might want to use different mocks for the same module.

In response, the team added a factoryFor function that would behave like so:

var subjectControllerFactory = thieve.factoryFor('subject_controller.js');

Now if subject_controller.js depends on modules like so:

define(['jquery', 'subject', 'grinder'], function($, Subject, Grinder) {
  ...
});

We can, with the factory, inject our own definitions for these modules:

var controller = subjectControllerFactory(mockJquery, mockSubject, grinder);

While these examples are simple, they are obfuscating some aspects. So lets rewind and see a practical example.

browser.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
define(['jquery'], function($, userAgent) {

    var ua = userAgent || navigator.userAgent;
    ua = ua.toLowerCase();

    var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
        /(webkit)[ \/]([\w.]+)/.exec(ua) ||
        /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
        /(msie) ([\w.]+)/.exec(ua) ||
        ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];

    var engine = match[1];
    var version = match[2];

    return {
        engine: function() {
            return engine;
        },

        version: function() {
            return version;
        }
    };
});

browser_spec.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require(['jquery.js', 'browser.js'], function(jQuery) {
    describe("Browser", function() {

        //...

        it('can parse the IE10 user agent string', function() {
            var browserFactory = thieve.factoryFor('browser.js');
            var Browser = browserFactory(jQuery, "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)");

            expect(Browser.engine()).toBe('msie');
            expect(Browser.version()).toBe('10.0');
        });

        //...
    });
});

The point of this code is to check the web browser engine and version. The code in browser.js is a slightly modified version of the deprecated jQuery.browser. Remember the part about how modules are basically shared scope? We can see this in browser.js, because any consumer of the module is going to get the exact same instance as the first consumer. The first time the code is consumed, lines 3-13 are executed. These extract the engine and the version. The scope within the module now has engine and version defined. All consumers after the first will have only the return value supplied to them. This return value is an anonymous object with engine and version methods. These methods are run within the scope of the module so they will of course have access to the already set variables.

Moving on to the test, it becomes clear that we need to be able to inject a userAgent string that typically would be accessed from the current browser via navigator.userAgent (as it is done on line 3 of browser.js). After getting the factory for browser, we inject jQuery and a known userAgent string for Internet Explorer 10.

At this point, I hope that it is clear what a JavaScript module is in the context of asynchronous module definition. The aspect of scope within the module shared across all consumers is an oddity, yet it has many benefits. If you want to try this approach, choose your implementation of asynchronous module definition carefully. Some offer access to the factory while others do not (in particular, require.js does not – wire.js is a good place to start). The team that I am working with on the large web application has made great progress modularizing the application using this approach. An interesting benefit is that the modular approach encourages good practices like splitting up your code into smaller pieces that are not tightly coupled. As the work to remove globals has gained momentum, the mood of the team has turned from pessimism to optimism.

Comments and Reactions

Modular JavaScript

I wrote a post for the 8th Light blog on Modular JavaScript:

Modular JavaScript

Comments and Reactions

Why I am excited about the health care insurance marketplace

I am excited because:

  • entrepreneurs will have affordable access to health care
  • separates health care from employer
  • might push for stronger reforms in health care
  • fixes the gap in coverage that impacts a lot of people in society
  • ends the “I/my family went bankrupt due to health problems”

It appears that the plans are going to be more expensive than going with an employer-subsidized plan. I suspect I’ll wait a year or two to see how things go but I hope that the health care reform succeeds. I think the trade off of raising the cost for some to provide access to all is worth it and I hope employers offer to subsidize plans from health care markets. A lot of it is still unclear to me but hopefully my employer will have more details soon. The current system is clearly not working for anyone except the very rich and sometimes the destitute.

Comments and Reactions

The Importance of Exercise

Almost two months ago, I got in a bicycle accident during my daily commute to work. It was serious enough that the front fork on the bicycle was bent backwards and I had cuts on my hands including my face. A couple days later, I went to Japan and had a great two week trip walking every day. When I returned, I wasn’t quite ready to get back on the bicycle again. It wasn’t that I was scare but rather angry at the whole experience. As time went on, I realized that I was growing more and more grouchy. I had more headaches than usual and had trouble sleeping at night. I didn’t put it all together until a couple days ago: I need exercise. After almost a year of riding my bicycle to work, my body has become used to regular physical exertion. At least that was my theory. I got back on the bicycle and I think that is indeed what happened. I was sleeping better after one day and now two days in I feel much more relaxed. So exercise for the win. I know we all knew that but…

Comments and Reactions

Professionalism and Finances

There are a number of topics related to the intersection of being a professional and finances. It is important to have a basic understanding of these ideas. At times, you may feel like you are asking for more money than you deserve however having a sound financial plan will make it easier to gain the fortitude to go outside of your comfort zone. You are a professional and you should spend the time to ensure your financial success.

Interviews

Apply for a couple of jobs a year. The point of doing this is to:

  • verify your worth on the job market
  • practice your interview skills
  • learn what is out there on the market
  • improve your negotiation skills
  • potentially end up with a new job

It is important to approach this task with the mind set that if you are happy with the outcome of the meeting you might just take the job. It is also important to not divulge the reason for your interview; as a professional, you have an obligation to continue to grow and considering new prospects is an important part of your career.

Note that it is important to be discreet about this activity. The point is not to go out for an interview and then come back to your employer and try to use that offer to negotiate a raise. The point is to know your value on the market so that you are in a good position to negotiate the salary you want for the position you want. Remember that a position is more than just a salary; it comes with expectations for the numbers of hours worked per week and may have odious requirements that make work miserable. In other words, not all jobs are created equally and some well paid positions may not make sense for you. Focus on what you want.

Salary

Read the basic ideas behind salary negotiation and go out and try them. At first, you may be intimidated. But fear not! The worst that can happen is that someone says no. An interesting blog post on this topic is: Salary Negotiation: Make More Money, Be More Valued.

Raises

As with all aspects of a job, raises are negotiable. Your employer may offer you a fixed raise but it is completely within your right to negotiate your raise. It is important to think carefully about this though because you need sound reasons for why you deserve a bigger raise than your coworkers. If you do not have these reasons or feel it is a bad time to negotiate a raise, simply accept the offered raise and move on.

There is no set time for negotiating a raise. It may be easier for your employer to entertain your proposition if it is closer to the annual review time but if you contribute significantly to the bottom line 6 months before that review, why not ask for a raise now?

Work Ethic

Focus on getting things down that contribute positively over clocking in your hours. As a professional, it is important to do the tasks that you might find annoying like working on an outdated technology, entering hours and submitting expense reports in a timely fashion. The point is that you can make a difference for both yourself and the company and both of you are better off for it. This sets things up well for reviews and negotiations on raises.

Internal Projects: Know When to Pass Responsibility on

In many jobs, you may end up working on something to keep the office going well just because you happen to have a particular skill. This is a good thing to do as long as you can do it well. If you reach a point where things outside of your control cause frustration and you can not fix things, there is nothing wrong with passing responsibility on to someone else. Someone with a fresh set of eyes or just a different personal relationship with whomever is involved can get things back on track. Do not let your frustrations effect your work.

Retirement

A basic rule of thumb is that you want to have saved enough money so that you can live on 4% of it per year. In other words, estimate your yearly expenses (say $30,000) and divide that by 0.04 (so $30,000 / 0.04 = $750,000). You will arrive at a figure that is pretty damn overwhelming. However it is within your reach. The earlier you start, the less you will need to save per year if you plan on retiring at a set age (say 65).

There is an opposing viewpoint here: once you accumulate enough wealth to potentially retire, you are now free to do whatever you would like to do. Your expenses like rent and food can be taken from the investment returns on your retirement account. It would be a great time to sail around the world or start a business. One important issue to consider though is health insurance.

It is also important to be somewhat pessimistic when considering the total amount you need to retire. Both in terms of what you think your annual expenses will be and in terms of your target amount. A couple extra years of work can give you enough buffer to weather serious financial setbacks.

Emergency Fund

In life, you will have setbacks. These setbacks may be financial in nature such as a loss of a job, needing to suddenly buy a new vehicle or some other unexpected event. A good rule of thumb is to have roughly 6 months of income saved in an account that you can access within a day or two. You should be prepared to weather these setbacks without undue stress so that you can continue being a professional in your daily life.

Additional Resources

  • Reddit r/personalfinance (note the sidebar with details on different types of retirement accounts)

Conclusion

There are many topics not covered here. It is important to understand your investment options. Research the difference between a pre-tax retirement account such as a 401k or Simple IRA and a post-tax retirement account such as a Roth IRA. Also note that I am not a financial advisor and the above is an outline of my understanding in some areas. While the 4% rule for retirement is widely known, some critique it as being too optimistic. As with all things, your experience may vary. However understanding these topics will set you on a path of financial success.

Comments and Reactions

Do I Have Too Much Experience to Become an Apprentice?

I wrote a post for the 8th Light company blog focusing on why I decided to take an apprenticeship even though I had a number of years of professional experience.

Do I Have Too Much Experience to Become an Apprentice?

Comments and Reactions

Low Cost ARM Computer

The RaspberryPi has a lot going for it but you can get started with a small ARM computer without the hassle of ordering from one or two venders and waiting for a bare board computer that also needs a power supply, case, memory storage, USB cable, HDMI cable, ethernet cable, etc. One alternative is a Pogo Plug. In the USA, you can order one for $20-40 from a number of sellers. It comes with a case, multiple USB ports, and a built-in power supply. You will need an ethernet cable but otherwise you’re all set. The one thing you can’t do with it easily compared to a RaspberryPi is hook it up to a display. But if you wanted a small Linux-based computer that sips electricity and is ready to go hardware-wise out of the box it fits the bill.

The software on the Pogo Plug can be easily changed to an ARM build of Arch Linux with specific instructions at archlinuxarm.org. After the install, I was able to hook up an external drive and setup bittorrent and other applications that are nice to have running all the time but don’t need a lot of computing resources. It is a shame leaving a full desktop powered on using 60-100 watts but a small ARM device like this uses something close to 10-15 watts.

Comments and Reactions

Software Craftsman Update

JavaScript

I’ve been working as a Software Craftsman with 8th Light for a little over seven months. The projects I’ve worked on have been interesting. All of them have involved JavaScript to some degree. I enjoy working with JavaScript but I’ve also started to see where frameworks can cause pain and where event-heavy code can become difficult to work with when the tools lag behind the practices. I want to spend some time digging into PhantomJS to see if it is possible to hook into events to make debugging easier. I spent some time digging around but it is early days.

I’m also interested in see if it would be possible to get more information on memory usage. Identifying DOM leakage is difficult. And of course identifying it on webkit is only going to help for browsers based on webkit however it would be a start. I would expect there to be more tools to help with this but I want tools I can run on the command line and have interfaces which can be used with tests.

In short, the developer tools with Chrome and other browsers are phenomenal however sometimes things seem more painful than they should be. Note that I am not blaming the tools but rather pointing out it seems like some don’t exist yet.

Apprenticeship

I took on a novice apprentice named Andrew beginning in January, 2013. It has been challenging his pipeline full while keeping up with all the other regular duties however it has also been rewarding to see the apprenticeship from the other side. He has made great progress with picking up TDD and with programming in general.

Waza

At 8th Light, we have Friday afternoons to work on whatever we would like to. I have been spending my time working on Abacus which is an invoicing system. The front end is written in Backbone.js with Mustache templates and the backend is in Ruby on Rails. The purpose of the system is to produce a PDF which has the invoice. I’ve started to move that to Clojure and iText after becoming frustrated with Prawn (Ruby PDF generation). Prawn is very capable however it has been hard to keep it from turning into a big ball of mud. I’ve also been working on putting Cucumber tests on the application using poltergiest (PhantomJS wrapped to use with Cucumber). The idea is to make it easier to simplify the backend so that it doesn’t have the requirement to generate PDFs and make it possible to refactor the application without breaking it.

Comments and Reactions