Working with AI

AIAgentsSoftware DevelopmentLearningPersonal

Something that I think about far too often, every single day now: What's the right way to use AI as a developer?

I'm a Senior Software Engineer. I like breaking down problems, crafting solutions over time, and having things automated and well-documented. And now I've been using coding agents for awhile at work and on personal projects. But how much is too much?

Lately I've been reading and thinking about the fact that if I rely too much on the AI to generate all the code for me, those skills will atrophy over time, which will:

  • Make getting a new job harder if it is still expected that you answer coding trivia in interviews.
  • Become continuously expensive, making personal projects have a real world cost that adds friction.
  • Contribute to long term environmental impact from the energy being used.

But at the same time, I feel like the genie has been let out of the bottle here and it is so hard to go back. Today I was working on a coding exercise I found online that involved recreating the "wc" tool from unix in a programming language of your choice. So I decided to try it out in Python (removing the "learning a new language" friction from the equation). I created a new folder, a solution file and then started creating a test file, intending to do a TDD approach. I immediately ran into so many questions:

  • If I want this to be a CLI tool, it needs to be packaged or I need to use the shebang convention to make it feel like a cli.
  • Do I want to use only the standard library, or my preferred choice which is the Click library to write the CLI parts?
  • For testing do I want to use pytest or unittest convention?

I eventually settled on click, unittest convention with the pytest runner, and standard pyproject.toml and pip. But this is where it gets interesting. I'm not typing out unittest code every day, so I had completely forgotten the syntax to setup unittest as well as how to test Click applications (there is a Clirunner class that can be used to test).

All this friction is natural when you work on a variety of things or come back to something after not doing it over time. But the process usually involves me finding a past example to copy and paste in to start from, or going to the various documentation sites for each library and copy/pasting in their boilerplate. But not anymore? It's so quick and easy to just use my coding agent to say "can you scaffold me a unittest class for testing this click cli" and work from there.

Is that better? Assuming the answers are correct (another topic for another time) It's certainly faster than navigating to each site and dealing with the inconsistent documentation quality of each library. For example, I personally find the Python documentation very frustrating to sort through, and end up going to something like realpython or stack overflow for examples.

The next step of course is going through the TDD cycle. So for the wc tool, the first step might be to add an optional parameter for getting the number of lines in the file. So I can either:

  • Already know the answer (not likely unless my job / passion is writing Click CLI tools every day)
  • Pull the documentation up and experiment
  • Ask the agent how I would add the parameter
  • Have the agent add the parameter

Which ones best? Obviously the first one is preferable, but not realistic for all problem domains. Option 2 is a really good skill to have and what I've been doing most of my career, but it is so much slower than option 3 or 4.

At this point in the exercise, I just went down this rabbit hole real fast, asking the agent a bunch of things and learning so much in the process:

  • Where does the filename parameter come in?
  • What if you want to support multiple files?
  • What if the file doesn't exist? (There is a built in mechanism I didn't know about as an optional parameter to the decorator?)
  • What should the output look like? How does WC handle it?

At this point I had so much information and example code I felt empowered to just start experimenting and adding to the solution I had. The friction of "oh that would be cool to add, but that might take a lot of work for this "toy" exercise" was gone and I was just having this excellent cycle of idea -> research -> test with the agent.

And that kind of explains where I'm at today with these agents:

  • I love having it help me get things started.
  • I (most of the time) ask it to code for me, and then (most of the time) read the output to see if it aligns with my years of understanding of syntax, design, and flow.
  • I am not afraid to tweak things or extend it.
  • I'm not ashamed to have it review what I've written or ask for feedback.

That second one is where I think the big issue is for me now. When the agent writes code, I'm not just blindly accepting it. I'm reading it the same way I'd review a peer's pull request. Does the logic make sense? Is this idiomatic? Are there edge cases it missed? Are there abstractions I'd do differently? Years of experience don't go away just because you didn't type the code yourself. They show up in how you evaluate it.

Could someone blindly ship generated code to production without reading it and cause real problems? Absolutely. But that's a choice someone (poorly) made. Used responsibly for research, for scaffolding, for access to documentation you'd have looked up anyway, and it is hard to overstate how much the experience changes. Better tools don't replace the craftsman. They just let them do better work.