Welcome to the Deep Medicine Digest! My name is Dr. Robert Toth and I run Theta Tech AI, a healthcare AI consulting firm. Click here to contact us about our professional consulting and AI services.
Has someone forwarded this article to you? Click here to subscribe to this newsletter for free and get AI content sent directly to your inbox:
In a world rapidly becoming dominated by AI tools, the process of coding is undergoing a massive overhaul. Thus, it’s more important than ever to deeply understand basic software development principles in order to engineer solid, robust, versatile systems. The AI coding tools will write the code for you, but you must know what to ask for.
There are a plethora of coding paradigms and principles out there, many of which I consistently use. DRY, SOLID, Single-Responsibility, Separation-of-Concerns, and Test-Driven-Development are just a few that come to mind. (Aside: You should include a list of these principles in your ChatGPT / Claude system prompt to remind the LLM to use them!) Today, I present a paradigm that I call “coding in layers”. This is not meant to supplant the other principles, nor create extra burden on large developer teams; rather, coding in layers is another tool in your arsenal to help you develop high quality code, both quickly and intelligently.
Coding in layers is meant to expose bugs early, provide tighter feedback cycles with end users, and build solid foundations early in the development process. It may take a bit of extra work upfront, but like all long-term thinking, building a proper foundation from the start will undeniably make your life easier down the road. When building in layers, features will be implemented sooner and bugs will be squashed faster. It may be boring to build the foundations from the start, especially when you just want to jump right in with a cool feature idea, but it’s the wise approach.
The core idea of coding in layers is to get everything connected, end-to-end, with fake data flowing through it from the very beginning. Then, as you layer on new features, you ensure that nothing breaks end-to-end as you develop. Each new layer should never break the previous layer, and the entire system should be usable (with dummy data if needed) end-to-end at every step along the development process. Layers should be granular, and regular end-to-end actual user testing should be done as part of the review cycle.
It may seem like overkill to connect everything when nothing is really functional yet, but it helps ensure the connective joints are in place before the code base gets too complicated. When it comes to API’s and data, use dummy data to simulate input / output. For example, create images with random noise for now, use an off-the-shelf AI model in the backend, provide placeholder text for future development, or ignore the content of the API responses.
While this may seem sloppy, what’s not sloppy is ensuring that an actual end-user has a functioning end-to-end system that displays something from the very beginning. And that a usable system never breaks as new features are developed. Sure, the core pieces may have plenty of holes that need to be filled in, but at least the software system will be connected holistically from the very beginning. Provide placeholders for now, return gibberish if needed, but just get everything connected!
The first layer is the base foundation layer. Make the frontend look as you want, and put placeholder div’s everywhere. Stand up a set of skeleton API functions that will be fleshed out later. Set up a simple CPU-based AI model on the backend for now. Just get it connected, and then keep it connected. Set up your dependencies, connect to any external services such as cloud dashboards or data sources, and make sure everything talks to each other. Build the scaffolding first. This base layer may not be fully functional nor feature rich yet, but it will be fully connected.
Only then, once the base layer is deployed and working, move onto new layers. Start swapping out placeholders for functionality, and watch the whole thing come to life as you build. A second layer might encompass setting up the frontend css and basic javascript code for user interaction. Your async calls will already be in place, and you simply have to design and move around visible components. And by building atop the previous layer, you immediately notice if something breaks, and fix it before it becomes a problem. By having active (internal) users constantly trying to test out and demo the system live, bugs are unearthed early and often. This creates a more solid foundation upon which to build. By building in layers, your code becomes battle-hardened and robust.
Later layers of development might include horizontally scaling the backend with load balancing and multiple servers, or setting up some serverless functionality with job queues. This can be tricky to get right, but fortunately you will notice if anything from a prior layer breaks without waiting until the end to test. By building atop a fully connected system from previous layers, hiccups in the backend become immediately apparent. By making sure previous layers don’t break, and the entire system stays connected end-to-end, you never have to go back and make sure the frontend talks to the backend correctly.
Remember: when building in layers, it’s important that you never move on to a new layer if the previous layer is broken. At every iteration along the way during development, ensure the entire system works end-to-end. It’s better to find and deal with bugs earlier, even if that means putting a pause on a new feature. Users like a solid system they can trust. It should “just work”, and a layered approach nearly guarantees that.
Most engineers write code component by component, partly connecting them in the middle of a development cycle, and then fully connecting them at the end. However intuitive this approach may be, it’s less powerful than a properly executed layered approach. Say good bye to spending a significant amount of time with integration issues or making the components talk to each other properly. Subtle bugs may arise in this integration phase that could have been entirely avoided by taking a layered approach from the start.
This is not to say to abandon the single responsibility principle. Each component of a layer should be well-encapsulated, do one thing and do it well, and be agnostic to the system it finds itself in with proper input / output definitions. That should still be done. You’re just building the basic core of a bunch of interconnected components first, and then fleshing them all out in congruence.
One of the most important benefits of the layered approach is that stakeholders (project managers, customers, clients, investors, test users, other devs, etc.) will be able to provide immediate, granular, and regular feedback, thus significantly increasing the odds that what is built will achieve product market fit. Instead of having to wait a few months for an initial MVP, and then weeks between changes, stakeholders will be able to see the system come to life live, providing course corrections along the way.
Features will be developed faster, bugs will be detected and squashed sooner, and the system will have been stress tested from the very start of development. This layered approach is repeatable, systemetizable, scalable, elegant, repeatable, solid, robust, and simply beautiful.
In a world where AI can write the code for you, you must know how to be an engineer, not just a coder. You will be asking the AI to help write code for you iteratively, piece by piece, correcting it as you go. Your skill comes in asking the tools to write the right code and the right time throughout the engineering process. And good coders know that it’s not about typing out the proper lines of code, but rather deeply understanding the system or software you are building. Writing the code is merely a means to an end.
See the big picture from the start.
Be wise: code in layers, and watch your code blossom exponentially!
Thank you for reading this article from The Deep Medicine Digest by Robert Toth, Ph.D.! Please forward this email to your friends and colleagues that may enjoy the content, or click the share button below!