The New Software Development Stack: Humans + LLMs

How Software Development Really Works

I’ve been developing software for 40 years. I’m mostly self-taught and because of that (and my personality), I’ve spent a lot of time thinking about the process of developing software, not just the code.

While most of my software development has been in small companies I founded or personal side projects, my day job is as an engineering manager at a large multinational. This has given me a different vantage point, observing two completely different engineers: the senior engineer and the junior engineer.

The fundamental difference isn’t years of experience. It’s which parts of the process the engineer is expected to own. A junior engineer is expected to implement a solution and test that their solution works. A senior engineer is expected to understand the problem, break it down into possible solutions, choose the best one, implement and verify:

  1. What is the problem we are trying to solve?
  2. What are the options for solving the problem?
  3. Which of these options is the best choice for this situation?
  4. Implement
  5. Did I fix the problem without breaking other stuff?

Senior engineers are responsible for all five stages; junior engineers only steps 4 and 5.

How We Used to Learn

When I started writing software, there were really only two ways to learn how to write code: read a book (magazine, etc.) or ask someone who knows (including take a class). With the advent of the Internet both of these options expanded exponentially. “Read a book” became “search the Internet.” Stack Overflow became the de facto method for figuring out how to do something you haven’t done before, or reminding you how to do something you’ve done in the past.

Asking someone you know also expanded exponentially. I could read blog posts by other developers, ask on social media, take an online course, and of course our ability to connect with other developers expanded as well. At big companies, “ask someone who knows” is known as pair programming. Two (or more) developers sit in front of a screen and talk about the code in front of them.

Both of these are inefficient. While searching the Internet is easy to do, it is rare to find the exact solution to your exact problem. Most of the time we have to transform someone else’s solution to solve our problem.

Asking someone who knows is also inefficient. Finding someone who knows and with enough experience with your problem and willing to give you time is one thing. Getting that help at the exact time you need it is a whole other problem. In addition, “asking stupid questions” is something most people don’t want to do with another human for fear of being judged.

What LLMs Change

LLMs resolve many of these inefficiencies. They can answer questions about your specific code, and they can act like a pair programmer with deep knowledge of your language, framework, and tools. LLMs have the patience of Job, answering every question, discussing endlessly, and, yes, even being polite for the stupid queries. No shame necessary!

LLMs don’t just change how we write code. They change how we think about solving problems.

Hello World!

A lot of engineers recognize the potential power but don’t know how to start. Some try things but have a hard time fitting it into their development processes. This is really hard to do, actually. We each build certain processes, certain ways of working, and the act of inserting an LLM isn’t so much inserting as rototilling.

So we need practical ways of putting a toe in the water. I’m going to propose five.

Help Me Do This

This is the simplest. It requires only the most modest change in process. Instead of asking the Internet how to do something, ask the LLM. To do this effectively you need to take the time to fully describe what exactly you are trying to do. This is a different muscle. Instead of reading something on Stack Overflow and attempting to determine how that turns into a solution to your problem, you instead need to fully describe your problem. The reward, though, is it gives you a tailor-made solution. This targets stage 4: implement.

Write Tests

The reward for writing tests is incredible. I can change code with impunity and know that my tests will make sure I don’t screw up other code, assuming I wrote the tests well.

LLMs have gotten exceptionally good at writing tests. Even if you are someone who enjoys writing tests, use the LLM to make sure you have fully covered your code. Feed it your code and your test suite and ask it to tell you what tests you need for complete coverage. This targets stage 5: making sure you don’t break other code as you make changes.

Review My Code

LLMs have been trained on the world’s public (and sometimes non-public) sources. Every line of open source code, every line of documentation, every example ever created for every programming language. Furthermore, an LLM is always available and, as I pointed out above, never judgmental. What a perfect pair programmer! Next time you write some code, when you think you are done, feed it to the LLM and ask it what it thinks, what it would improve or change, ask it if you missed anything obvious (even if you know you didn’t). This targets stages 4 and 5: implementation and did I fix the problem.

Investigate the Best Solution

Senior engineers often perform the first three stages almost subconsciously. After a while the process is just baked into what they do. One of the issues, though, is sometimes senior engineers don’t stop long enough to listen and ask questions, ensuring they fully understand the problem before deciding on a solution. Junior engineers don’t generally perform these stages, but there is no faster way to become a senior engineer than to start. 

In this stage, instead of proclaiming your detailed solution to the problem, start with the problem itself. Explain to the LLM the problem you are trying to solve. Tell it not to write code; instead, ask it to discuss the best solution. Don’t share with it what you think the solution should be, but instead use your knowledge of the space to push at the edges of what it thinks is correct, redirecting it, expanding the scope, testing the edges of your own thinking in the process. For juniors, ask it what potential approaches we should take and why it picked the one it did. If the problem isn’t clear, ask it what else we might need to know in order to even decide on a path ahead. This targets stages 1, 2 and 3.

The Whole Enchilada

The real unlock happens when we start using LLMs for all five stages. While I wouldn’t start here — get comfortable with LLMs at your own pace and the level to which you are willing to let it participate — allowing an LLM to direct all five stages unlocks a speed and leverage that wasn’t possible before.

Start with Investigation as defined above. Align on a solution together, then align on an implementation plan. Work on each step of the plan together. As it changes code review what it is doing. I personally like to have it write in its own chat, leaving me to move it into my code base. That allows me to align it with my expectations.

I often go back and forth with it a few times, getting it to refine its approach. I test as I go manually. At each step I also work with it to write unit tests. And as I complete all implementation steps, I have it write integration tests, many of which I specifically define and direct it to write.

I’m still responsible for all the code. It’s my job to make sure that what outcome I wanted is what I actually got, and to ensure my test suite is complete. Same as it always was!

Metamorphosis

I’ve now built complex projects I never would have attempted on my own. I’ve been able to develop complex SQL queries when before I could really only accomplish simple ones. I’ve learned a ton about SOC compliance and row-level security for securing Postgres databases. I’ve built groundbreaking mathematical engines that drive a new way of doing math in the AI age. And I’ve done all this by myself. In the old days there were plenty of ideas I had and discarded because I did not have the skillset to build them myself.

Now? It’s incredibly liberating to realize that the only thing holding me back is my imagination.