Sinclair Target

The Trellis: A Gardening Metaphor for Software Engineering

Sep 12, 2025

In any substantial software project, there are usually two categories of code. There’s code that actually implements the program—whether it is an application, a library, a game, or whatever—and code that exists to support the other code. This second category of code includes automated tests but also benchmarks, deploy scripts, Makefiles, and CI jobs.

The second category of code exists because we want to ensure that our actual, implementing application code has certain stable properties. We want to make sure that it is correct. We want to make sure that it is memory-efficient and performant. We want to make sure that we can build and deploy it in a repeatable way without regressions. One could imagine a world in which we do all this manually and there exists only that first category of code. But instead, since we mere humans are at a severe comparative disadvantage when it comes to doing something over again the exact same way, we enlist the computer’s help.

I’m not the first person to suggest that building software is like gardening. I know the metaphor from The Pragmatic Programmer, by Andy Hunt and Dave Thomas. They probably weren’t the first people to suggest it either. Building software often feels like an organic, chaotic process. Sometimes it’s easier to start building something and see how it grows than it is to plan it out in detail ahead of time.

I want to extend the gardening metaphor to help clarify the distinction between “actual” application code and supporting code. And sort of give some color for why the supporting code is useful.

I’m handicapped here by not knowing anything about gardening. But even in this age of pervasive AI hallucinations one can still use the internet to learn the difference between a lattice, a trellis, a pergola, and an arbor. Did you know that these are very much not the same thing?

Anyway, a trellis is what I was thinking of. In gardening, a trellis is a framework of wooden (or perhaps plastic) slats that you can use to support a growing plant, either because it isn’t strong enough to stand on its own or because you want to guide it in a certain direction. The plant can grow its way up the trellis using both the vertical and horizontal members for stability. Probably you have seen one of these before.

I think by writing automated tests, benchmarks, and the like, we are setting up a trellis for our application to grow on. I like this image very much. When I try to account for my attachment to it, I can come up with two reasons.

The first is that “trellis” gives a succinct name to this second category of code that encompasses more than “test suite” or “test harness” can. Automated tests are obviously important. But there’s a reverence for automated tests that doesn’t extend to other kinds of supporting code. If instead we think of all our supporting code, tests or otherwise, as part of the “trellis,” that makes room for other kinds of code that can sometimes contribute as much as or even more than tests toward our confidence in our core program.

I’m reminded here of YouTuber Sebastian Lague, who creates ingenious little visualizations for the graphics programs he writes, which otherwise would be hard to verify. In this video, he uses several simplified 2D models to confirm that the algorithms he’s implemented for his 3D ray tracer are correct. In this other video, he creates a low-resolution canvas to render a glyph onto in order to test a font rendering algorithm for artifacts. These little models aren’t automated tests, since they don’t succeed or fail without manual inspection, but I imagine they are still indispensable for 1) debugging regressions and 2) improving his understanding of his own code.

Most people don’t have successful YouTube channels and don’t create anything as visually impressive as Sebastian Lague’s visualizations. But it’s not uncommon to write little scripts to validate assumptions about how something works or how it scales. It’s easy to think of code like this as throwaway code. But it’s part of the trellis! It should be committed to our repo and live right alongside our test code.

The second reason I like the trellis image is that it helps us think clearly about the relationship between our application code and our supporting code over time.

The trellis code should be rigid. It should outline the general, consistent direction our “living” code should grow in without over-determining exactly how that code will grow. It can be extended when we have confidence about how we want to build it out and when the growth of our living code demands it. But otherwise it doesn’t change that often.

Our living code should be able to change all the time. The trellis is the structure that allows our living code to twist and wind and curl in every direction. We can prune away large parts of our living code knowing that it can safely regrow on our trellis along new and surprising paths. Our living code can be flexible.

I cannot for the life of me find where I first read this, but one definition I’ve seen given for software engineering is “building software and then changing it over time.” This was contrasted with just writing a one-off program, which doesn’t entail a level of complexity that would require software engineering practices to combat. I love that definition, because it highlights that the hard thing about software is change.

The gardening-with-a-trellis metaphor encapsulates how we can best do battle with change. Computers are amazing! We can use them to write bendy, protean programs that can accommodate inevitably changing requirements. We can also use them to write programs that check that certain invariants continue to hold true. Doing both is the key to growing a happy plant 🌱.