Staying Lean — Keeping Clean Code

Ricky Vetter
Social Tables Tech
Published in
7 min readJul 30, 2014

--

One of the core values at Social Tables is Stay Lean. We focus on creating a product that people love. Time, energy or money spent outside of making our users ecstatic with our work is wasted.

Here on the product team, we swiftly resolve bugs before they become major problems and create features that enhance our customer’s user experience as quickly as we can. We have to be able to move at a quick pace and our code has to stay clean and lean.

Social Tables is built as a monolithic app which has grown to cover a wider functionality than I think all but our most optimistic followers thought 3 years ago. It showcases the power of technologies like Node.js and SVG, and serves as a fantastic example of one application serving the needs of many different audiences.

There is nothing inherently bad about monolithic apps. In fact, I would argue that the structure of our app helped us to grow at the speed we did. However, as we grow it is becoming more and more apparent that the structure of our app is holding us back.

Pros of the monolithic startup

Creating an MVP is all about speed. It’s about creating the simplest thing with the smallest budget that will convince people that they want to see more. Having a single app which houses all of your code helps this process in so many ways.

  1. There is one code base and you learn it intimately
    You only have so many resources. It’s important that everyone on the team knows the product inside and out.
  2. It focuses work on key business problems
    When you are constrained by one single project and are forced to make decisions based on what will make that one product better, it is easier to see which needs are superfluous and which needs are needs.
  3. Speed doesn’t have to correlate negatively with quality
    If all of your code exists together, new code doesn’t have to work perfectly in every situation — just the one that you know it will be used in. This means faster iteration and better improvements without hurting the quality of your product.

When growth and structure are at odds

  1. You spend too much time focusing on the structure of the code and no one can claim to understand the way all of the pieces come together.
  2. You are fighting the structure of the code to add features that you really need, but that don’t have a “place” for. If there are features that need to exist that can’t be added in a sensible fashion, then your company has grown well beyond the MVP stage and needs to be able to separate functionality in a more concrete way.
  3. New code works perfectly in the situation you wrote it for, but breaks functionality you didn’t even realize would be affected.
  4. New team members have trouble contributing until they know all the quirks of the code.

Some companies will never need to leave a monolithic app structure. Maybe the functionality of your app is limited in scope and there is no real reason provide a wider set of services, or maybe there is some other reason why horizontal growth would be ineffective for your team.

If you see yourself in this situation — as part of a company that has reached no bottlenecks of creation and innovation in a monolithic app structure and likely won’t in the near future; then the rest of this post is a practical solution to a problem that doesn’t apply to you. Go and work on solving exciting problems in an awesome code base that works for you!

Becoming a collection of modules

Social Tables has pain points caused by our app structure. We work with a Node-based application that has been in production for two years. As such, there are a number of complexities that require you to be in direct contact with the code to understand. We are hiring new engineers on a regular basis, and teaching them the ins and outs of our app has become more time consuming. Changes that we make have unforeseen effects in seemingly unrelated places. It has become more difficult to create features that delight and amaze our clients in the time frame that we have set for ourselves.

We have reached this point where change is necessary, and as a solution we have decided to break our code into separate, easy to manage, testable modules.

Our Solution

Right now, our goal is to take as much of our code as makes sense and break it into separate repos. We can then require them into our different Node processes as modules. Some of the questions we asked ourselves were:

How to we decide which code is worthy of breaking out into a module?

We want our modules to speed up our ramp-up time by separating concerns and by creating black boxes that don’t require any cognitive load for our developers to work with. We want to get to the point where each of our Node modules are as implicitly trusted as the npm modules we rely on to do our work. This means we decide what should and should not be separated into a new repo by requiring three attributes:

  1. It is being used in multiple processes
  2. It is handling one specific concern
  3. It is or can be built in such a way that new processes can rely on it without looking at or modifying code

How do we do this without halting our usual work cycle and to continue to improve the product from a client perspective?

We are working on this project slowly over a period of time. Each of the modules that we want to split apart has a different size and importance, so we will need to balance our other work and the importance of each of the project pieces to get them done quickly. This is the least thought-out piece of the puzzle and one that we will be working on finalizing before moving forward with other modules.

The solution seems relatively simple. Although, I’m sure we’ll be able to update you on how horribly optimistic I am once this is finished.

Define a process

First, we define and separate each of our Node processes. That is, everything that is or should be its own process or provides its own service. Right now, we have the website as a separate server, the main application, and a small number of applications that serve a specific purpose and help Social Tables run smoothly.

Build modules

Once we have separated a process, we include every module that existed in our monolithic app that our new process needs to run. If that module is an npm module or a git repo, then we’re set! We add it to package.json and move on to the next one.

It gets a little messier if that module is custom code that we wrote for Social Tables. First, we find out if that module is being used by a different process. If not, we remove the code from our main repo and give it its own module. If the code is being used in multiple places, we decide if that app can be open sourced and we make a new repo accordingly. We then look at the dependencies of this new app and continue down an ever growing rabbit hole.

Run the module

After the recursive dependency hunting is complete, the success should eventually bubble up to our new process which will throw a different error (hooray) and allow us to continue on! Eventually, the process will run and some code may need to be modified to be sure that it doesn’t rely on the high coupling that it had as a custom module in our app. It seems like a tedious process, but we are hoping that it will help us navigate our code quickly and efficiently and quicken our pace when adding new features to our codes.

Test and document

Our app is moderately well tested, but many of the modules we have created are currently untested, or under-tested. Beyond testing, we need to document the ways to interact with each module in a detailed and specific readme. Our goal is to only open a module’s code if we are explicitly working on that module. In all other situations, the readme and running of tests should be as dirty as we need to get — which is to say, not dirty at all.

Use the new process in production

Once the process and all of its dependencies have been tested, we can place it into production. This means writing a deploy script and making sure it has been removed from our main app’s architecture. At this point we should have a number of black box modules that we can trust work in all situations and rely on documentation to use in the different ways we may need them.

The goal of this process is to stay lean as a product team and make sure that going forward we are able to make the best products possible at the rate that our industry demands.

Thanks

I’d like to extend a huge thanks to Matthew Whittemore and to the rest of the Social Tables product team for their help with this post. As the newest member of the team, I had very little to do with the planning of this process. I was lucky enough to start the implementation of it and have enough experience to write about it. This plan was conceived by Matthew with input and guidance of the rest of the engineers.

Questions about our strategy or comments about how we could do it better? Tweet any of the fantastic engineers at Social Tables and we’d be happy to talk with you.

--

--

Ricky Vetter
Social Tables Tech

@socialtables engineer, @chipotle advocate, @colorado native