Startup engineering: Delivering value while remaining adaptable

Django Shelton
The Hedgehog Blog
Published in
6 min readFeb 3, 2023
Django Shelton, Lead Engineer @ Hedgehog

We know from lived experience how it can be a balancing act building an engineering team in the ever-changing start-up environment. So, we’ve decided to jot our thoughts down on paper, not only to share our views on the strategies that we have found to consistently deliver value, but also to serve as a guide for us in the future.

Keep engineers as close to the business as possible

At Hedgehog, engineers have a high level of ownership and responsibility. We didn’t simply hire extremely talented engineers, then just ask them to deliver tickets without asking questions. We hired engineers who understand and are motivated by delivering a product that broadens access to investing in real assets, then gave them ownership of problems which need to be solved. It’s up to them to speak for the business, understand the context of the problems we are trying to solve, and then make decisions which help the business solve those problems and achieve its goal.

All of the other points I’ll touch on in this article speak about the same idea of reducing cognitive overhead and saving engineers time. Engineers can then be as effective as possible by focusing on business problems. In turn, engineers understanding the business mean they will also understand how it might change in the future, and force them to consider this while designing and building the solutions. This reduces the amount of rework required overall, and incentivises engineers to make their work flexible and extensible.

Use integrations, take advantage of tech debt, and document key decisions

When building our MVP, which we launched in the summer of 2022, we were always going to stay true to our vision, but we knew we couldn’t hit our deadlines by doing all the work ourselves. We ended up integrating with Veriff, Onfido, AWS Cognito, Securitize, and DocuSign all as part of the MVP. This is a lot of integration work, but still a lot less work than building in house what these companies and experts have done already. However, integration work is often messy and requires compromises, as a 3rd party’s opinionated platform or data model sometimes won’t play nicely with our own view of the world. When combined with a desire to launch as soon as possible, there were several compromises we had to make along the way, or in other words “tech debt” we needed to introduce.

At Hedgehog, we view tech debt as a powerful tool to speed up development, learn quickly, and iterate. However, unless it’s managed well, tech debt can easily get out of hand and cripple development teams. They can lose context around why decisions were originally made, and become paralysed by indecision or fear of breaking something. The main way we mitigate this is through documentation. By documenting when key decisions are made, and the context around those decisions, we gain the ability to easily undo those decisions months or years later. We use Lightweight Architecture Decision Records to document these key decisions (and compromises). This gives new team members crucial context for why things are the way they are, and gives the whole team a valuable tool to make changes in an informed and safe manner. We’ve seen these documents provide invaluable context when we’ve come to revisit decisions we’ve made on payments, authentication, and tokenization in particular. All of these projects are now owned by people who were not on the team when the original decisions were made, so the documentation provides a key resource for them to make new decisions and move forwards.

We also avoid addressing tech debt unless it’s having an immediate impact. In our view, if a piece of code is never going to be touched again, regardless of how messy, out of date, or upsetting it may be to work with, then the tech debt there is effectively irrelevant. It only matters when the piece of engineering in question is going to be adjusted, built upon, or replaced. It’s at these points in time when we choose to address tech debt. Effectively the tech debt becomes Schrödinger’s cat — it only becomes a problem when you observe it, up until that point it is neither a problem nor acceptable.

This ensures we continue to focus on what matters most for our users, while also avoiding the trap of never addressing tech debt. We talk about tech debt openly and honestly with the rest of the company as well — rather than hiding tech debt or hand waving it away. We think it’s important for everyone to be bought into our way of working, and to trust that way of working. So far this has served us well and allowed us to strike a good balance between delivering quickly when it matters, and addressing critical tech debt when it matters.

This approach to integrations, documentation and tech debt allows us to support changing requirements much more easily. Rather than aiming for a perfect solution, or something we built completely ourselves, we accept imperfect solutions and solutions built by others to begin with, and deliver these quickly. Over time, we will often discover these solutions are not needed or are replaced entirely with an alternative. Anything else that remains part of the product will be improved over time when it matters. We avoid wasting time trying to be perfect the first time around, and let the success of our product dictate what is important to spend more engineering time on.

Keep it Simple Stupid: Monolith vs Microservices

Microservices are a very common architectural pattern these days, splitting an application into multiple small and lightweight services. This approach has a number of benefits, including agility, flexibility, and ease of deployment. All of these things sound great for an early stage start-up, but they must be weighted against the additional complexity introduced. The infrastructure overhead is not trivial, and you need to understand your domain well to be able to decide on a sensible set of services to introduce. There is also the additional complexity which comes with observing and debugging a distributed system, which is a hard problem for even huge organisations to get right.

Instead of implementing microservices, we’ve decided to build our application as a modular monolith. This allows us to keep our codebase modular and domains separated, while avoiding the extra infrastructure complication of microservices. It also simplifies all our deployment pipelines, and gives us flexibility to adjust our domains as they evolve and emerge. When a good candidate emerges for a microservice (e.g. if we notice particular workloads slowing down the overall application which need to be scaled individually, or some data needs to be held in a more secure location which the rest of the application shouldn’t have access to) our code is already logically separated and refactoring this into a microservice should not be a huge lift. We’ve already built our first microservice to handle the most sensitive of user data in an entirely separate database. Essentially, we are treating microservices as an infrastructure problem rather than a code problem.

We try to apply this general approach of Keep It Simple Stupid (or KISS) wherever possible. This frees up brainpower for understanding business problems, and makes our technology simpler to adjust when a changing business requirement does emerge.

Engineering team now driving business strategy

We firmly believe in the digital transformation of financial markets. Luckily, as we now have an engineering team fully ingrained in the business, we can look to them to help deliver value as we build a platform designed to integrate in the customer journey of other financial service providers..

Over the last 12 months, we’ve managed to grow from a single engineer, to a whole team which is fully integrated within our business. We’ve done this by keeping engineers as close to the business as possible, taking a pragmatic approach to building ourselves and accepting tech debt, and keeping things as simple as we can. This has led to us having an engineering team capable of driving strategy and delivering value on its own, rather than being a factory which enables the rest of the business. We see this as key to delivering a successful startup in our industry, and we’ll continue to share our progress on this journey.

--

--