Starting a New Technical Project (and sticking to it)

The main image for this blog post

So many of the side projects I’ve started before have had a way of of stalling out after the first few weeks. I’ll get excited about a new project idea, and have an initial period of constantly looking forward to building it, and eventually get to this early-middle phase where motivation fades and writing code becomes a slog. I’m starting a new project at the moment and it’s made me think about the usual patterns I encounter and what I can do about it this time to give myself more of a chance.

I wanted to build a non-trivial side project to learn Go, and to learn more about payments as a problem space, so I’m building Pay Once - a payments API designed to be safe to call repeatedly when networks, clients, or servers fail and retry requests. Two articles that I’ve found really inspiring for how I’ll approach this have been Mitchell Hashimoto’s blog post on his approach to starting large technical projects (decompose large projects and see regular results), and Tom Preston-Werners post on Readme Driven Development (Readme and spec up front).

Both of those articles have some excellent insights, but I think my approach is somewhere in the middle, which is what I’ll write about here. I tend to have the most success when I’ve broken large projects down into smaller chunks, leaned on regular tests or demos for visible feedback, and designed something up front to use as a guardrail for me to keep coming back to as I make progress. For this project, I’ll use a README as my initial design artefact, which will define things like the rough shape of the project, its milestones, and the chunks I’ll build.

Using a Readme to Get a Feel for the Product Early

It’s easy to write technically solid software that doesn’t feel great to use. I want fast feedback on whether the product I’m building feels right, and a README gives me a low-commitment way to define the public behaviour of the product and spot if anything feels off before I start building.

This forces me to think from the user's perspective. I have to explain to someone else how to actually use this thing. For me, this means it should have at least a clear statement of what it is and who it's for, a snappy get started style demo, and a few API examples showing the main interface.

The main risk here is over speccing and falling into the perfection trap. The point at this stage isn’t to layout a complete technical spec. I’m trying to put together a high level overview, with as few architectural commitments as possible, and just enough externally visible behaviour and constraints so that I’ve got a sense of direction when I’m writing code.

In my Pay Once README, I start with a 60-second demo. Even before any internals exist, it forces me to answer “What does the first minute of using this service feel like?”.

Writing this upfront gives me some useful design feedback - if the flow reads awkwardly or confusing then I learn that immediately and I can change the shape of the API before I commit to any serious implementation details.

Initially I thought the GET call would come back with a status code and call it a day, but looking at it felt a bit bare and didn’t inspire much confidence. Seeing that made me realise I needed more, like a real timeline of events saying “here’s where we are, here’s what’s happened”.

Breaking it Down Behaviourally

Tackling big projects in one go can feel overwhelming and kill motivation. There are so many unknowns, edge cases, and technical tradeoffs that I don’t know about yet. My way of dealing with this is to break the project down into smaller, independent sub-projects.

For me, the key ingredient here is to not think of this technically, and instead try to think of it behaviourally. If I’m a customer reading the README for the first time, is it clear to me what steps are involved, what it’s doing for me and how I should interact with it? Separating the actual user interface and problem I’m trying to solve from the tech that’s involved makes it easier for me to break the tasks down into more logical chunks, as I’ll naturally start to see the project in steps. I’m trying to focus on the inputs and outputs - what needs to happen, rather than how it happens.

Payment flows are fairly linear, which makes it a little easier for me to plot each step. For example, If I just look at the happy path for handling payments, I can see there’s a natural sequence: create an initial order attempt → authorise the attempt → capture the payment.

For Pay Once, the behaviour maps quite clearly for me to demos I can see myself running:

Once I’ve found those demo boundaries, I’ll start by trying to list the smallest possible pieces I think obviously need to exist for those demos to work. I don’t know all of the details of the project up-front, but even a rough idea is fine - I can swap pieces out as I go and the overall picture becomes clearer. The next thing to do is to ask “which of these can I build in isolation and still see a real result?”. Answering that question narrows the scope of work I’m tackling and the project feels much more doable.

When I say “real result”, I mean something I can run and observe like a demo script that produces an output, or a test that proves a behaviour. I’m looking for slices or work that are small where I can get some sort of visible output. Again, I’m not thinking technically at this stage, only behaviourally **from the callers point-of-view.

So now I can look at each stage and see if I can break it down further. Looking at the first stage - “Create a an idempotent payment attempt”, it’s clear that I can break it down into the following:

  • smoke - I can run the service and hit one endpoint.
  • create attempt - I can create an attempt and get an attempt_id.
  • idempotency (create) - I can repeat create safely (same attempt_id).

Much more approachable than “build a retry-safe, idempotent payment initiation service”.

See Results Early, and Regularly

I know that if I don’t start seeing results soon, I’ll lose steam, get bored, and abandon the project. In front-end heavy projects that’s not usually a problem because your feedback loop is immediate - you can see if the square you’ve made is the same as the square you’ve designed. With backend projects like this though, especially where the domain or technology is unfamiliar, it can be a bit more tricky. In order to get these results, I’ll lean on a mix of automated tests and runnable demos.

For each piece I've decomposed from the initial demo sketch, there should be a handful of tests I can define where a pass validates progress. Writing these tests tells me just as much about what not to work on as what I should work on. I'm only trying to do as much work as needed in order to move on to the next piece on the road to a demo. I can use the tests to help me define what “good enough” means for me to be able to move on to the next thing.

In Pay Once, a good enough test might mean “do I get an attempt ID back after hitting /attempts?”. Building to pass this test might initially just mean I’m returning a UUID e.g.

The idea with building towards demos, in addition to being validation that all the pieces up to this point fit together, is that they will show me a working result which is hugely motivational. For Pay Once, I expect that the demos will initially be pretty basic but still provide some valuable feedback. Once I’ve got a set of running tests for all the components that build the first demo, I should be able to spin up the server, hit the /attempts endpoint twice with the same idempotency key, and see that I get the same attempt ID back. There’s something about seeing all of the hard work fitting together nicely that’s incredibly satisfying, so demos will be the key checkpoints for me to build towards.

In Summary

The process can generally be boiled down to: put together a rough sketch of the project, use the behavioural flow of what you’ve sketched out to set the boundaries for decomposition, break those behavioural chunks down further into isolated pieces of work and then build towards passing tests and working demos, using the README as your guiding document as you progress.

At the time of writing, I literally just have the README written out - after all this is about getting started and staying motivated more than anything else. Although this project is far from ‘Done’, by writing this all out upfront my hope is that I’ll have something to steer my learning and building as I work through the project.

I owe a lot of what I've written about here to Mitchell’s post (and his blog in general tbh), reading that truly made it ‘click’ in terms of what works best for me.