Slinging code from Montreal

Hard and fast rules that mostly work

Hard and fast rules that mostly work

Hello, friends! Today I have a rapid-fire collection of some practices and ideas that have helped me stay happy and productive while building and maintaining complex software products. As your product gets larger, and more and more customers rely on your code to solve their business problems, things can start to go wrong in interesting and surprising ways. 

 

Small repos

You know about stacks? The computer science thing about a pile of stuff where you add to the top and remove from the top (LIFO - last in first out)?

You have a stack in your head, and you use it every time you're investigating a bug or designing a system. You add to it each time you have to follow a function call or add another abstraction to the design. As the codebase gets bigger, the surface area of the stuff you might need to keep a mental reference to grows super-linearly. Not a fun time.

So break up your codebase. If you're using git for version control (you are using git, right...?), check out submodules. Any decent modern programming language should also provide a packaging system, giving you another way to break up your monolithic repo into chunks that can be maintained, updated and tested on their own. 

All this decomposition comes with a catch, though. You'll need to become disciplined about exposing and enforcing explicit contracts between components of your newly maintainable monolith. I'll speak more about contract-driven development in a subsequent post, but suffice to say, this principle applies at multiple levels.

 

Small Pull Requests

Sensing a theme here, huh? Once again - bigger means more opportunity for mistakes to slip through. Big PRs are hard for your teammates to review. People get tired of reading a never-ending wall of green and red, and they lose that incisiveness that you, as the submitter, depend on.

In busy projects, a PR will start drifting away from master pretty much immediately. The longer it lives, the more error-prone and difficult the merge back into master could potentially be, especially if you're working on a high-churn area of the codebase. Keep 'em small, keep 'em focused.

And for the love of god, if your PR adds or changes expected behaviour of the product, provide tests to document and enforce the new behaviour. You're investing in your future sanity and that of your colleagues. Its gonna pay off handsomely, trust me. You'd be surprised at how often nobody at the company can remember how some aspect of the product is supposed to work.

 

Eggshell Testing

Here's my heresy: I've never been huge on TDD. Instead I subscribe to something I'm calling 'eggshell' testing. When building out a new piece of user-facing functionality, I'll add an integration test driving a headless browser that exercises it. Checking if the UI behaves as expected, it validates the feature as a whole.

Once the happy path works and is validated by the integration test, I'll harden the inside of the shell with layers of tests, first at the controller level then into the service and model objects involved, depending on complexity of the code and subtlety of the logic. In this way I'm able to fearlessly refactor the internals, all the while secure that pressing the right button will do the right thing when I finally deliver the feature.

Ultimately we're craftspeople, not robots - rarely are the nuts and bolts as simple and clear-cut as that, but it's a methodology I aspire to stick to as closely as possible.

 

Red means stop the line

If you're relying on your test suite to let you know when something is amiss, a red build has to mean a stop-the-line event. No new code is committed or deployed before this situation is remedied.

Sometimes the test is invalid (testing the wrong thing or out of date), in which case we need to fix or delete it. If the test is fine, then we've broken some piece of expected behaviour. Usually the culprit is the most recently merged PR - sort it out and get back to glorious, comforting green.

Most importantly, don't forget to be happy in the knowledge that you saved a customer from experiencing a bug, and have a slightly less brittle test suite to boot!

 

This list is not remotely complete, but the above have been some of the practices that I've noticed provide great return on investment, as we aspire to mastery of this craft. It's all about progress, not perfection.

The master value of software teams

The master value of software teams

Persisting lambdas for object-specific logic in Ruby and Rails

Persisting lambdas for object-specific logic in Ruby and Rails