Writing Code In Small Pieces - Vertical vs Horizontal Development

June 27, 2021 · 3 min read

There are many ways you can write your code.

We have best practices and linters, opinionated frameworks and architecture patterns.

The very big and very small decisions. Stuff you decide on the team-level.

Then there's a middle ground. That's what happens while you implement a larger feature or a smaller service by yourself.

The result is what your team sees. But the process is how you develop that code.

Everyone has their own way of writing code, and I believe we can learn from each other if we share our internal processes more.

Some definitions to start:

  • Vertical development means you make a fully, end-to-end working solution first (however few features it has) and then extend it. Example: you set up a full Node.js Express server, then add a route to it, and then test everything together.
  • Horizontal development means you make one piece of the software work and then add another "layer". Example: You write the functionality for the "business logic" first, test it with a unit test, and then integrate it into a web server. This can also be done in a test-driven way.

This seems like a fuzzy distinction, but with a larger example it'll make more sense.

Writing a Tic-Tac-Toe Game

You can find the rules of the game here: https://ccd-school.de/en/coding-dojo/application-katas/tic-tac-toe/.

In summary, we're developing a terminal program that displays a Tic-Tac-Toe game in text form. This program expects three inputs:

  • A coordinate like A0 or B2 for the player to place their symbol
  • next, which starts a new game
  • exit, which exits the program

After starting up, it automatically sets up a new game for the player.

When the player makes a move, the game AI directly makes the opponent's move (which can be as dumb or smart as we want).

Let's look at how we can implement this horizontally or vertically.

Vertical implementation plan

Going vertical, our goal is to get a program running with a terminal loop. We want to start it; it asks for input; on enter it prints new text and asks for new input. Ideally, the text is our initial Tic-Tac-Toe field.

Development now would go as follows:

  1. You start researching how you can get a CLI with a terminal loop running
  2. You implement it, showing the same output regardless of input
  3. You test it manually

If you got this working, you can add state management, handling of keywords like new and field names like B1, add input validation, and so on.

Horizontal development plan

When developing horizontally, we start with the "business logic". I'd use a test-driven approach and develop a class that manages our whole game state, takes inputs, and outputs the game text for the console.

Development plan:

  1. Create a unit test that instantiates a class called TicTacToeEngine
  2. Implement an empty class TicTacToeEngine
  3. Create a unit test that calls a method called toString() on a new instance and expects the empty game state string
  4. Implement the toString() override
  5. Create a unit test to call a makeMove() method with an allowed enum (e.g. Moves.A0)
  6. Implement the makeMove() method to pass the test
  7. Create a unit test that checks calls makeMove() and then toString() and expects an updated state string
  8. … and so on until we meet the specification from above

You can also create unit tests for invalid inputs, e.g. making the move "A0" two times in a row should throw an error. At one point, you should also write tests to expect an AI move (two changes) after making a player move.

When you got a fully working game engine, it is time to wrap it into a terminal app that instantiates a game on start (and on the new keyword), listens for input and calls the makeMove method with correct enums, outputs the state to console, and so on.

Why choose one development method over the other?

There are benefits to getting a fully working application as fast as possible. And there are benefits to having a small, tested, and reliable component ready for integration.

It comes down to preference and situation.

Horizontal development is only possible for small "horizons".

I prefer to start vertically by making "something" work, so I know where the different application parts interact and how the internal APIs look like. Then, if it makes sense (i.e. I have a well-defined component like a Tic-Tac-To engine), I go horizontally and implement one component fully.