How to keep a template for new projects.

•

•

A Starter You Actually Maintain

I spent three weeks at a logistics startup trying to build the “perfect” automated scaffolding engine, complete with custom CLI tools and complex CI/CD abstractions. It was a massive waste of time. I thought I was being efficient, but all I actually did was create a high-maintenance monster that broke every time a dependency updated. Most people approach the question of how to keep a template for new projects by trying to automate away all the human thought, when they should really be focused on reducing the cognitive load of the repetitive stuff.

I’m not here to sell you on some shiny, over-engineered generator or a complex suite of internal developer platforms. I’ve learned the hard way that if a template is too rigid, it becomes a tomb for old decisions, and if it’s too loose, it’s just a pile of boilerplate. I want to show you how I manage my own project starters: by keeping them boringly predictable and easy to strip down. I’ll share the specific, low-overhead ways I document my setup so that when I start something new, I’m actually writing code instead of debugging my own scaffolding.

Table of Contents

Standardizing Project Structures Before the First Bug Hits

Standardizing Project Structures Before the First Bug Hits

When I started out, I thought a “project” began when I ran `git init`. I was wrong. A project actually begins with the decisions you make before a single line of business logic is written. If you don’t start with standardizing project structures, you aren’t actually building a system; you’re just accumulating a collection of slightly different ways to misconfigure a database connection. I’ve spent too many afternoons migrating service logic from one repo to another only to realize the folder hierarchy was so idiosyncratic that I had to spend three hours just finding the entry point.

I eventually moved away from manual setup and started using basic project scaffolding tools to enforce a baseline. It’s not about being a pedant; it’s about reducing the cognitive load. If every microservice I spin up has the same `/cmd`, `/internal`, and `/pkg` layout, I don’t have to think about where the logs go or how the middleware is injected. I can just focus on the actual problem. The goal is to reach a state of efficiency in project setup where the boilerplate is invisible, leaving you with nothing to do but solve the problem you actually got paid to fix.

Efficiency in Project Setup When Everything Is on Fire

Efficiency in Project Setup When Everything Is on Fire

There is a specific kind of panic that sets in when a stakeholder demands a new service be live by Monday, but you haven’t even finished configuring the CI/CD pipeline for the last one. In those moments, you realize that manual setup isn’t just slow; it’s a liability. I’ve spent too many hours manually copying `docker-compose` files and tweaking environment variables, only to realize I missed a single line in the `.gitignore`. This is where project scaffolding tools actually earn their keep. They aren’t about being fancy; they are about ensuring that the baseline—the boring, repetitive stuff like logging configurations and health check endpoints—is identical every single time.

When things are moving fast, your brain tends to skip the “small” details, like how the service handles graceful shutdowns or how it exports metrics. By leaning on workflow automation for developers, you offload that cognitive load. I don’t want to think about how my project structure should look when I’m trying to solve a logic error; I just want the scaffolding to be there, solid and predictable. If the foundation is automated, you can spend your energy on the actual business logic instead of fighting with your own boilerplate.

Five things that actually matter when you're building a template

  • Don’t template the business logic. I’ve seen people try to bake specific domain rules into their base repos, and it’s a nightmare when the next project needs to do something slightly different. Keep the template focused on the plumbing—the CI/CD pipelines, the logging setup, and the folder structure—not the actual product.
  • Include a “How to destroy this” section in your README. Every template should come with a clear explanation of the architectural trade-offs you made. If I chose a specific database driver because it handles connection pooling better for our scale, I need that written down so the next person doesn’t swap it out for a “shinier” one without realizing why it was there in the first place.
  • Automate the boring stuff, but don’t hide it. Your template should come with a basic Makefile or a set of shell scripts to spin up the local environment. If a new developer has to spend four hours manually configuring their local Postgres instance just to see a “Hello World,” the template has already failed.
  • Treat your template as its own project, not a static file. I keep my project templates in their own repository with their own lifecycle. When I find a better way to handle secret rotation or a more efficient way to run our linting suite, I update the template repo. If you treat it like a “set it and forget it” document, it’ll be obsolete within six months.
  • Ship with a working, empty test suite. There is nothing worse than starting a new service and realizing you haven’t even decided how to mock the external APIs yet. I always include a few basic integration test examples in my templates. It sets the expectation from minute one that if it isn’t tested, it doesn’t exist.

What I actually learned from the wreckage

A template isn’t a rigid set of rules; it’s a baseline of “known good” configurations that prevents you from making the same configuration mistake for the third time this year.

If your template takes more than ten minutes to get a “Hello World” running in a staging environment, it’s too heavy and you’ll eventually stop using it.

Treat your template like code, not documentation—if you find yourself manually fixing a dependency or a build script during a new setup, update the template immediately or you’re just building technical debt into your next project.

The Long Game of Maintenance

The Long Game of Maintenance in coding.

At the end of the day, a project template isn’t about being a perfectionist or following some arbitrary industry standard. It’s about reducing the cognitive load when you’re already dealing with a heavy feature load or a looming deadline. By standardizing your directory structures, automating your CI pipelines, and documenting your configuration choices early, you aren’t just saving a few minutes of typing; you are building a safety net for your future self. I’ve learned the hard way that if you don’t codify these decisions into a template, you’ll inevitably end up reinventing a slightly broken version of the same wheel six months from now.

Don’t aim for a “perfect” template that tries to solve every possible edge case—that’s a rabbit hole that leads to nothing but maintenance headaches. Instead, aim for something useful and predictable. A template is a living document, a piece of code that evolves as you encounter new production failures and realize what you missed the last time. Treat it like a tool in your kit rather than a monument to your current expertise. If it makes your next deployment slightly less stressful, it’s doing its job.

Frequently Asked Questions

How much of the template should actually be code versus just documentation and configuration?

I’ve learned the hard way that if you over-template the code, you end up fighting your own scaffolding. If the template includes too much business logic, every new project becomes a struggle to strip out what you don’t need. Keep the code limited to the “boring” stuff—CI pipelines, linting, and container configs. The rest should be documentation. I’d rather have a README that explains why we use a specific database than a codebase that tries to do it for me.

At what point does a template become "too heavy" and start slowing down the very people it's supposed to help?

A template becomes too heavy when you start fighting it just to write a simple “Hello World.” If I’m spending more time stripping out boilerplate, unlinking unused CI/CD pipelines, or deleting pre-configured microservices I don’t need, the template has failed. A good template is a head start, not a straitjacket. If the setup process feels like an obstacle course instead of a shortcut, you’ve over-engineered the solution and created a new kind of technical debt.

How do you handle the inevitable drift when a project evolves away from the original template?

The drift is inevitable. Eventually, someone adds a new dependency or tweaks a build script, and suddenly your “standard” template is a lie. I used to fight this by trying to force everything back into the mold, which just annoyed my teammates. Now, I treat the template as a living document. If a project evolves a better way of doing things, I don’t fight it; I backport that change to the template. The goal isn’t perfect uniformity, it’s preventing the same mistakes from repeating.

Should these templates live in their own repository, or are they better off as a CLI tool?

I’ve gone back and forth on this. A standalone repo is easier to start—you just clone it and change the names. But I’ve learned that “cloning and renaming” is how configuration drift starts. I eventually moved toward a simple CLI tool. It feels like more work upfront, but being able to run `new-project –service-type=api` and having it inject the right linting rules and CI pipelines automatically saves me from those inevitable “oops, I forgot the observability boilerplate” moments.

Tomasz Wierzbicki

About Tomasz Wierzbicki

I am not interested in arguing about frameworks. I am interested in why the thing broke at two in the morning and what would have prevented it. Most of what I know came from being wrong in production and writing it down afterwards.