I, like many of you, am wrestling with how to best apply AI to my career as a software engineer. As we continue to adopt to AI-assisted coding, there is becoming more and more evidence that we may be limiting the ability of our future selves to own and support software systems.

In my last post, I made the argument that by writing this blog by hand, it forced a deeper understanding of the material, and resulted in something I was proud to call my own. Leveraging AI for writing removed both of these benefits, and stripped the satisfaction I get from having a blog in the first place.

“[T]he goal of writing is not to have written. It is to have increased your understanding.”

Alex Woods

The same principle can be applied to writing code. I have always believed that writing prose and writing code have more similarities than differences, and it follows to reason that if writing prose increases understanding, writing software would do the same. By writing software by hand, you increase your understanding of the system. Yet it is no longer realistic to write all code by hand. It may not even be realistic to write 80% of code by hand. So, how can we use AI to both become more productive, meet the needs of the business, and increase our understanding of the software we are tasked with building and supporting over time.


One idea for managing the complexity of software is Functional Core, Imperative Shell. Purely functional code makes some things easier to understand: because values don’t change, you can call functions and know that only their return value matters and they don’t change anything outside themselves. This design has many nice side effects. For example, testing the functional pieces is very easy, and it often naturally allows isolated testing with no test doubles. It also leads to an imperative shell with few conditionals, making reasoning about the program’s state over time much easier.

By placing pure functions at the core of your application, the core business logic and complexity of your application can be easier to reason about and understand. Imperative code, useful for dealing with the messy realities of the real world like database connections or file I/O, can safely form the shell of your application.

Borrowing from these ideas, one way I’m starting to think about AI-assisted software development is through the lens of “Human in the Core, AI in the Shell”, where a human strives to write, or at the very least, deeply understand, the core of a software system, and leverages AI for the long-tail of work required to build a full system: writing APIs, integrating with third-parties, writing tests, reviewing for security, and all of the other numerous things that are required to build a full-featured and production ready application.

With this model, the human stays informed, knowledgeable, and adds value to the core of the system, and is still ultimately responsible for becoming the subject matter expert, while still staying productive by leveraging AI for a large number of peripheral tasks. This doesn’t necessarily mean that all code in the core of the application is hand-written, only that the human in the loop fully understand the entirety of the core. My biased opinion is that writing at least some of the core yourself as the best way to fully understand it, but ultimately it is up to the human in the loop to use their judgement on when AI-assistance dilutes this understanding or enhances it.

Ultimately, the question isn’t whether we should use AI. It is not longer realistic not to use it. But we can also decide how we choose to engage with it. If we allow it to replace the activities that build and increase our understanding, we risk becoming unable to support or maintain complex systems.

In practice, this means thinking intentionally about where to draw the line. We can safely let AI handle the scaffolding, the integrations, and the repetitive glue, but when it comes to mission critial pieces of the application, ask yourself: do I truly understand this piece? Could I explain it? Could I debug it at 2 a.m.? If the answer is no, that’s your signal to step in.