Monday, July 27, 2026

Vacay Mode

"Do I still know how to code?"

I'm fortunate enough to have a vacation for so long that I eventually stopped thinking about AI, LLMs, Python, Work or even emacs. Traveled with the family by Train to nice places in Europe.

Our first stop was Berlin, and we arrived with the Night Train from Stockholm. We spent two days in the City with so much modern history. Then, a fast train (299 km/h was the fastest I noted) from Berlin towards Garmisch-Partenkirchen. A lovely village in south Germany with amazing surroundings!

Next stop was our main destination: Lago di Garda, Italy. We got there by traveling through Austria and the Brenner Pass. We spent a week in this wonderful place. Instead of Models or Prompting, there was Magnificent views and Pizza.

When traveling back home, we took a similar route going towards München (Munich). We spent one night there and continued to Hamburg the next day with the nice and fast Deutsche Bahn InterCity Express Train. Just a few hours in Hamburg before getting on the Night Train back to Stockholm again.

I don't recall me thinking anything about Python, while strolling the streets of Hamburg or when having a beer in a Munich biergarten.

During this time I have read two great books, unrelated to software or work: Perfection, by Vincenzo Latronico,
and Four stories by Han Kang (a collection of short stories, probably only released in Sweden).

The short stories are some of the most beautiful
and creative things I've ever read.

I'm in Stockholm again. Catching up and slowly getting my mind set for work mode. It is significant how different I feel now, compared to the first days of the vacation. I haven't checked, but I bet my heart rate is slower than before. I still have vacation and there's plenty of time to read another book unrelated to software or work.

A recurring thing, when returning from a vacay is that I worry about if I have forgotten things: What was I doing before? Are the emacs keyboard shortcuts still in my muscle memory, or gone forever?

Do I still know how to code?

That last question feels different this time. 2026 has been the Year of Agentic Engineering for me. This means basically no typing, but instead describing, steering and reviewing. From a human skills perspective, there's something lost here. I think typing is a combination of muscle memory and an enabler of creative thinking. Maybe that's what we developers call getting into the Flow? Eventually, it is a high chance of forgetting about the actual syntax when not using it as before.

Today I developed and released a new version of the Open Source tool that I maintain. It was a small change, mainly a Developer Experience improvement thing, but the implementation meant sorting and reducing nested data structures. A fun problem to solve! I was almost about to prompt my Agent to solve this, out of pure laziness. But I have thought about this before: doing Open Source development on my spare time could be an opportunity to practice, to keep and improve my own skills.

I ended up coding it myself, but used the agent to do the reviewing of the changes. I think this way might be a good balance between Agentic Engineering and not losing useful skills as a human. It's like a workout. So I will continue this workflow. Agentic Engineering at work - coding Open Source on my spare time.

Photos by me. Top photo: an early morning in Riva del Garda, Italy. After a workout and a short swim. No one but me and the birds at the beach.

Saturday, July 4, 2026

Practice what you Preach

I'm the maintainer of the Python tools for the Polylith Architecture, that adds useful tooling support for teams having their code organized according to the Polylith Architecture. So far in 2026, I have made seven Feature releases (i.e. minor versions) and a bunch of smaller ones (i.e. patches).

One of the features that was added this year is the possibility to inspect the coupling between individual parts of the code. In Polylith, code is logically grouped into something called bricks. For Python, these are what we know as namespace packages within the same repo.

Each package has an __init__.py, and you can define the interface of the package there. This is optional, and it is perfectly valid Python to access the modules within a package directly. In Polylith, a stricter approach is recommended: explicitly define the things that should be exposed outside of the package (aka brick).

      from my_package import my_function

      __all__ = ["my_function"]
    

The new feature is exactly about this: visualize and validate the usage of brick interfaces across the repository. With a simple command, you will find out if code is bypassing the explicit interface of a brick. This is still optional, and as long as it is valid Python you can safely ignore this. There's benefits of having code that interacts via explicit interfaces, such as refactoring - it's very easy to move code - and low-risk of breaking things when changing the code within the brick.

      # the existing command visualizing coupling between bricks
      poly deps

      # the addition, that will inspect the brick interfaces
      poly deps --interface

      # or inspect a single brick
      poly deps --interface --brick the_brick
    
Vizualize coupling, bypassed interfaces or circular dependencies.

What about circular dependencies between bricks?

Remember, a brick is a namespace package and that means that it is on a level above the individual Python modules. It can be valid Python to have two packages depend on each other, if the imports are for different modules in the packages. But this is an indication that something might be wrong with the Design. I would even go so far calling it a Bug. Two packages that import code from each other could be refactored into three, where the shared things are in a separate package. This is how the Polylith tool identifies circular dependencies between the bricks. The same command as for validating interfaces is used for this feature (poly deps) and it will inspect the code in the entire repo, to look for any circular brick dependencies. The interface lookups and the circular dependency checks are both internally using the builtin AST parsing.

Control your Agents

Many teams use agents today during development and knowing that agents can produce a lot of code in a fast pace, these two features are very useful if you want to avoid any unexpected side-effects of importing Python code without clear boundaries. Agents can use the poly tool too, just like when doing linting and testing. Static analysis is a great thing to have in the Agentic Engineering toolbox. The Polylith tool includes agent skills, so your agent will understand how to use it.

Practice what I Preach

I knew from before that the source code of Polylith tool bypassed interfaces in a couple of places, but I thought this is fine since it is valid Python. But I realized that my future self will thank me if these risky parts are fixed now and not later. Also, it is good if I actually practice what I preach being the maintainer of the Polylith tool for Python. So I fixed that. In addition, I also learned that two bricks in the repo had circular dependencies caused by a recently added feature. In this case, it was a circle of several bricks that initially made me worried. But the solution was simple: move the parts that caused the circularity into a different brick. I already had an existing brick that made sense to host the couple of Python functions that caused the circular dependency.

After refactoring: no interface violations, no circular dependencies.

Related posts and resources



Top Image generated by Mistral AI with modifications by me.

Sunday, June 28, 2026

Reflections: 6 months of Agentic Engineering

As many developers today, I'm using agents on a daily basis for software development. At work, we went all-in Agentic Engineering in early 2026 and for me it has worked well.

Not perfect, not 10x, but well and the shift was surprisingly easy. I quickly found that good coding practices are still applicable and relevant. I’m not into that vibe coding stuff, I care too much about the output. What has been occasionally overwhelming is the uncertainty about The Next Step - just like many developers out there I have thought about how we, the Humans, will keep up with the speed of the Agents?

Here's some reflections after about six months of developing software according to Agentic Engineering.

Human reviews have not become the bottleneck. From my experience, human reviews was a bigger bottleneck before Agentic Engineering. We have made efforts in our Team to improve the review flow, to avoid the need of constantly reminding people to review and approve changes. We have made the review process itself more efficient than before.

For me, the ratio of Development time vs Planning and Reviewing is about the same as before. I find myself designing and developing solutions the same amount of time as before the agentic era. In the beginning, I thought it would be the opposite!

I haven't run into the problem of having my agents produce hundreds of pull requests on a daily basis. I know there are people out there doing amazing stuff over the weekends with agents, but I'm not one of them. I haven't yet found a need to rebuild an existing SaaS tool from scratch with AI.

Quality is important. I think it is even more important than before, because an agent will be confused by complex code. Not only that, it will likely produce code that is equally complex (there's research about this available). This will be a downward spiral. Here's where Agentic Engineering is different from Vibe coding. A vibe coder treats the code, the output, as a black box. The risk with that is software that is very difficult to maintain. An agentic engineer keeps their eyes on the incremental result, and steer the agent during the process. The Human decides what Quality is, what is Readable code and what is Simple code (just like before).

Static analysis tools are a great to have in the agentic toolkit. Linters and Unit Tests are as important as before. I have added tooling that reports on complexity and readability issues, and use the Code Health MCP for that. Without the need of human interaction, the agent will verify its result while working and adjust it until the score is healthy before it reaches my eyes. Having static analysis tools doing this kind of work instead of an AI will reduce token spend. Reducing spend will be important in the future as the cost of tokens likely will increase.

Always bet on Open Source. Back in the days, as a .NET developer (when it was closed source and Windows only), developers were heavily dependent on Microsoft. Not only for the framework, but also for most of the tooling. I don't want to go back to that era, and see the same kind of vendor lock-in pattern with the AI providers of today. My tooling of choice is Emacs and the provider-agnostic tool Eca. When Anthropic has its downtime issues, I just switch to Gemini and can seamlessly continue while my colleagues at work are frustrated and post Claude is Down memes.

After six months, I still have a skeptic view on agents and LLMs in general. I simply don't trust the output. But at the same time, Agentic Engineering has made my workflow more efficient and I can more confidently step in to developing things outside of my comfort zone.

Related posts



Top Photo by me from Hoburgen, Gotland in Sweden.

Sunday, June 21, 2026

An Agent- and Human-Friendly Architecture

"Software Architecture in the agentic era?"

What's needed for an architecture to fit well in the agentic era? Probably many things, but I would say at least simplicity and available context as two very important things to consider. Even if it for sure was not the purpose from the beginning (it was born many years ago), Polylith is about that and is a good choice for Agentic Engineering. That's a nice side-effect of keeping things simple and having all necessary context at your fingertips, which is the guiding star for this software architecture.

A year ago, agent-friendly architectures was not something I had thought about at all. Today, a lot of things has changed. But many ideas about good software remains valid.

If you haven't heard about Polylith, here's an elevator pitch: the main use case is having Microservices in a Monorepo, and share code between the services.

It's an architecture with useful tooling support and great development experience for both humans and agents. All of it Open Source. I'm the maintainer of the tooling support for Python, and teams can use it with their favorite existing tools (such as uv, poetry, pdm, pixi, maturin, hatch). The Polylith tool also includes a set of agent skills, that will add knowledge about Polylith in general and the available commands to your agent. Recently, I also added skills that are specific for migrating a single-repo Python project into a monorepo. I hope that this will help teams to more easily start using this way of structuring code.

The code in a Polylith monorepo is basically building blocks, just like LEGO bricks. Some bricks are small, some are bigger and some are a combination of other bricks. All code lives in a well-structured Monorepo (without symlinks or complex custom setups). The code is organized into smaller reusable parts with clear boundaries between them. The tooling will notify if these boundaries are bypassed or if references are circular.

LEGO bricks and code, what's the connection? In Python, a file is a module. One or more modules in a folder is a package. One or more packages can be combined into a feature. In Polylith, this is called bricks. There are two types of them: components and bases. Components are the main building blocks in Polylith. This is where the business logic lives, the actual features and functionality. Bases are the entry points to your apps or services. A base should ideally be thin, and delegate the business logic to components.

Microservices are great, but the standard setup can introduce new problems: code in many places, duplications, shared code as libraries (that means even more repositories) and different versions of everything. That can be a lot of things to maintain, for agents too.

With a monorepo structured as a Polylith, the agents will have all the necessary context in one single place. It's right there! Agents perform better when things are simple, very much like us humans do. Besides having all needed context, an agent can also use the Polylith tool just like a human would. This will save tokens, and likely speed up the development process even more. The agent skills of the tool will tell the agent how and when to use it. Having all code there at your fingertips is also great for Test & REPL Driven Development, making coding both joyful and interactive.

Link to the Python tools for the Polylith Architecture repo.

Top Photo by Mitchell Hartley on Unsplash

Sunday, June 7, 2026

Refactoring with AI?

With Agentic Engineering there's an opportunity to refactor legacy code, but this aspect of AI isn't that that much talked about and seems to be overlooked by teams out there. So far, the main thing with AI and agents has been to build new things in a fast pace.

After several months of practicing Agentic Engineering, I've realized that code quality - or code health - is more important than ever. Agents get confused by complexity, just like humans. You've probably seen it (I have too) and now there is also research backing it up. Keeping code simple is a good guideline also in the agentic era.

This is where I believe the Polylith Architecture is a good fit, for both humans and agents. The original idea of Polylith was to keep things simple and great Developer Experience for Humans. As a nice side effect, Agents will probably like this setup too. An agent will have the context it needs to build new features from the existing well-organized code in the Monorepo.

Since before, the Polylith tool for Python includes a set of agent skills about the commands and about Polylith in general. But what about migrating an entire existing single-repo project into a Polylith Monorepo? With AI, many steps could be automated here.

I recently added additional skills to the Polylith tooling that are focused on migrating and refactoring an existing Python project into an Agent friendly Monorepo. The migration skills handles the extracting of application code, restructuring and also refactoring of large components into smaller ones.

The agentic flow for the migration of a project is:

  1. discover
  2. analyze-imports
  3. extract-to-base
  4. update-imports
  5. prepare-project
  6. verify-stability
  7. isolate-base-and-big-component
  8. split-big-component
  9. extract-standalone-modules
  10. isolate-shared-and-project-logic
  11. distribute-wiring
  12. split-component-internals
  13. refactor-tests
  14. definition-of-done

Also, the project-specific tooling (such as Project & Dependency management, linting and type checking) is migrated and converted according to what’s already in the monorepo.

Example: you use uv and ruff in the Polylith Monorepo, and migrate a project that uses Poetry and flake8. The migration will convert those to uv and ruff.

I have tested the skills with Claude (Opus 4.7 and 4.8) and Mistral (mistral-large and devstral). One of the tests I ran was converting the FastAPI repo (just for fun and to test the skills out) into Polylith.

my migration prompt

In this repo, there is @./.agents instructions about the Polylith Architecture and the "poly" tool. There is also migration instructions, and those are important here. I want to migrate the project #./project/fastapi according to the migration instructions.

Before running the prompt, I copied the contents of the FastAPI repo into the Polylith projects folder. I also removed the existing .git folder in there, to avoid any confusion during the agentic work.

Hint: the more unit tests the project to migrate has, the better!

The skills are part of the polylith-cli, and you can copy the contents of the .agents file or use a tool like library-skills to add them to your monorepo. Agents are unpredictable and different models behave differently. If you decide to try this thing out, please let me know how the skills works for you.

You'll find info about Polylith for Python here.

Top Photo by Blend Archive on Unsplash

Monday, April 6, 2026

A workflow for Agentic Engineering

"- Claude, build the entire Platform from scratch. Make no mistakes."

In the agentic engineering community, some think that 100% test coverage will guarantee the quality of generated code. I think that relying too much on automated tests is problematic, because testing is difficult. I'd rather review the actual source code. More importantly, having an active part in the development of it by steering the agent(s) in real time.

I usually develop a feature step by step, to not focus too much on upfront planning. I used to do this before the agents joined the development process, and have found that it works well today too. When beginning with a new task, I often have some clear ideas and some vague ideas about how to solve the problem. By starting with the things that are clear, I can postpone thinking about the implementation details of the more vague parts of the overall feature. I can worry about that later, when I have learned more. During the development, the how and what to implement will likely change. This is a natural thing, as you learn more along the way and understand more about the actual problem to solve. I guess this is the basic idea behind Embrace Change (from the Agile Manifesto).

I haven't yet felt any need to set up larger Agent Orchestrations for the kind of problems that I solve. It mostly seems like overdoing it, doesn't it? I don't know about you, but we are not building or reinventing an entire E-Commerce or Social Media platform every day. The things we do is on a much smaller and human-friendly scale. I think the idea about 100% test coverage might be about scaling up fast and the assumption that agents will produce so much code, that it becomes impossible for humans to grasp.

An Example

I recently developed a new feature that involved several repos, a combination of Python backends and Next.js apps. I decided to do this in smaller steps and began with the most straightforward step, which in this case was one of the backend services that I already know well. I had a pretty clear idea on what needed to be changed in there and added a simple Solution Design into the ticket.

For context: I used the same ticket for all the development of this particular feature. Each plan an agent produced was submitted as a comment to the ticket. I have automated this with an MCP server connected to the issue system that we currently use at work. It seems like having the relevant data collected in the ticket made the upcoming tasks clearer for the agent(s). Each task begun with a new context, but I instructed the agent to read the ticket that contained the problem to solve, the solution design, previous plans and linked pull requests before planning how to implement things.

It felt like things went pretty smooth. However, the very first task needed several iterations. I realize now that even if the basic setup of that particular repo was straightforward, the service has evolved over time and the structure of it has diverged. I think this is quite common in long-lived repos. This confused the agent, so I practiced the stop-the-line principle by rejecting generated code, correcting and steering the agent in real time. I did this when I noticed that the implementation (the code) went into a direction I didn't like. My agentic tool of choice, Eca, recently added support for a new steer command that is very useful for this kind of workflow. You can change the direction, or steer, while the agents are working. It is not always necessary to halt all ongoing work, sometimes a friendly nudge in the right direction is enough.

Sometimes, unexpected things happen: the other day, the main model I use was unreachable about half the day (again). Oh no. But I just switched to a different provider and continued the work! This is another thing where an Open Source and Provider-agnostic tool like Eca shines.

Another thing that I noticed was that the short summary the agent produced for each Pull Request covered the whole feature and the particular details pretty well. The data in the actual ticket was probably helpful for this kind of task too.

I've been advocating Test Driven Development (TDD) and the sibling REPL Driven Development for many years. The real-time steering, the stop-the-line principle with origins from The Toyota Way could be an Agentic Engineering version of the Test Driven approach. It's about fast feedback loops. What do you think?

Top Photo: that's me pretending to do something important on the cell phone, taken at Åreskutan, Jämtland, Sweden.

Sunday, March 22, 2026

The tools of an Agentic Engineer

A lot of great things have origins from the 1970s: Hip Hop redefining music and street culture, Bruce Lee was taking Martial Arts to the next level and the initial development of something called editor macros (also known as Emacs) was happening. I was born in that decade, but that's purely coincidence.

My choice of primary development tool since a couple of years back is that editor from the seventies. It is my choice of development for Python, JavaScript, TypeScript and Lisp dialects such as Clojure and elisp. And today, as an agentic engineer, it turned out to be a great choice for this kind of software development too. With the rise of various CLI, TUI & Desktop based tools for AI development, it would be reasonable to think that this ancient code editor would become obsolete - right?

Not if you knew about the innovative Emacs community. It is driven by passion, support from the community itself and Open Source. These components are usually more resilient and reliable long term than the VC driven startup culture. Emacs is part of the greater Lisp community, where a lot of innovations in general take place. The Clojure community is cutting edge in many aspects of software development including AI.

More Agents

One thing that I have noticed lately is that the more I get into Agentic Engineering, the more I use Emacs. When the focus has shifted from typing code to instruct and review, I have found use of Emacs powers I haven't really needed until now. Tools like Magit (git) and I'm also learning more about the powerful Org Mode. I didn't care that much about Markdown before, but now it is an important part of the development itself. So I just configured my Emacs to have a nice-looking, simple and readable markdown experience.

"More Agentic Engineering, More Emacs"

With Emacs, I use a great AI-tool called Eca and with it I am not limited to any specific vendor for agentic development. Vendor lock-in is something I really want to avoid. The combination of Eca and the power tools mentioned before, makes a very nice Agentic Engineering toolset. Eca is actively developed and has a lot of useful features and a very nice developer experience. It supports standards like AGENTS.md, commands, skills, hooks, sub-agents and use a client-server setup in the same way as the language server protocol. It is Open Source and not only for Emacs. Have a look at the website for support of your favorite editor or IDE. By the way, Eca is developed in Lisp (Clojure).

I have my Eca-setup shared at GitHub, and have also some contributions to the Eca plugins repository.

Human Driven Development

With this setup, the human reviewing can happen in real time, and doesn't have to wait until the end where the amount of code too often is quite overwhelming. The human developer (that's me) can quickly act when noticing that things takes a different route than expected, in a similar way as the stop-the-line principle from the Toyota Way. This is a lean way to reach the end goal quickly: deploying code that is good enough for production and adds value.

I have found that many Agile practices in combination with developer friendly tools fits well with the ideas of Agentic Engineering. Even though I've seen worrying signs of a return of the Waterfall movement.

To summarize: the result of my new Agentic Engineering development-style is that I haven't put my IDE to the side - it's at the very Center of the agentic workflow.



Top Photo by me, taken at Åreskutan, Jämtland, Sweden.

Sunday, March 15, 2026

Agile & Agentic Engineering

"Don't fall into a waterfall-style of software development."

Our industry is quickly adapting to the new ways of working and we are redefining what it means to be a software developer: Agentic Engineering.

It's not the same thing as Vibe Coding, and probably why I have had such a surprisingly smooth transition recently. As I see it, vibe coding is about treating code as a black box: it doesn't really matter how things are put together. The only thing important for a vibe coder is the output. As a passionate TDD and REPL driven programmer, this doesn't feel right for me today. Tomorrow, I don't know. Right now, I care about how things are constructed. More importantly, I need to have some understanding about the code itself to get ideas on how to change and improve the output.

This is where Agentic Engineering fits in: a structured way of developing software, where the human developer takes an active part in the process. It is not only about high level architecture or designing the solution, it's also about having the possibility to direct the agents into producing actual code that the human can understand and approve. For me, this is about keeping things simple and concise. Functional. Doing things in small steps, i.e. solve a problem by breaking it down into smaller parts. Experiment and learn along the way, step by step, rather than making big plans upfront. This is the core of the Agile movement.

It's well known that LLMs can produce verbose chunks of code and forget about important things. But it is possible to take control of that part as an agentic engineer. Similar to the stop-the-line principle (from the Toyota Way) where you can halt the production if you identify an issue. Take action (clarify or give new instructions) and then proceed. With these skills, agents can produce chunks of code that are just right. And functional.

From what I've picked up in the developer community lately, there's an increased need for structured work in the new AI landscape. This makes a lot of sense. What surprises me is the conclusion that we should start doing more planning upfront, writing detailed specifications before any code is written. This is what the Plan, Execute, Test sounds to me. I am just misunderstanding, or is this a Modern Waterfall movement?

Plan, execute, test might be the correct workflow for an agent, but not for a human. Planning in the beginning is difficult, because in the beginning we have very little knowledge about the thing to develop. Instead, we could learn what and how to develop something along the way. We can also pick up bits of why along the way too, but it's good to have some understanding about that specific part early in the process.

If the Plan was incorrect we will be 10x, but with 10x waste. Agents produce code fast, and it might not be that big of an issue as before if we need to throw away the result and start over. This is new. The difficult part is throwing away our plan, our design that we've invested in, and start all over again. This drains human energy. Once a plan is set, it can be a too big mental effort to break free from it because things have been decided already. Big planning upfront might sound right, but it is a trap. A vague Jira ticket description to begin with is not necessarily a bad thing.

The challenge as agentic engineers is to 10x the value, and not end up in 10x waste. Essentially making us more product and value focused than before. In short: build the thing right, also build the right thing.

How can we do that? Explore, learn and adapt are words I would like to see as part of the Agentic Engineering definition. Plan a little bit upfront, just enough to get started, but no more than that. Continue exploring, planning and adjust as the work proceeds. Get things out fast so you can collect feedback (logs, errors, usage) and learn what to adjust. That's Agile & Agentic, a Lean and Agentic Engineering workflow.



Top Photo by me, taken at the top of Åreskutan, Jämtland, Sweden.

Saturday, October 25, 2025

Please don't break things

"Does this need to be a breaking change?"

During the years as a Software Developer, I have been part of many teams making a lot of task force style efforts to upgrade third-party dependencies or tools. Far too often it is work that add zero value for us. It adds significant cost, though. As a user of third-party tools, you don't have much choice. Even if you might feel productive as a developer when implementing these forced changes, think of all the other stuff you could do instead to improve your product or platform.

The great tools from the community, most of it Open Source, is something we should be thankful for, and appreciate the efforts made by the people out there. This is a plea to have extra attention when making changes that will affect your users. I am also an Open Source maintainer, and try hard to avoid changes that doesn't add value for the users.

An example from Python: uv

warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead

The change itself makes a lot of sense. There's a new PEP standard for how to declare the dependencies only needed for development. Most Package & Dependency management tools out there already had their own implementation of this feature and it is probably a good thing to use the standard. From a user perspective, this only means that we need to make changes in all our Python projects. My suggestion is: why not support both options?

Maintainability vs Value

From a tooling developer perspective it is understandable that you don't want to maintain several ways of solving a problem. What about the Developer Experience of all the users of the tool out there? Imagine the teams maintaining many projects with 10, 40 or even 100 different Microservices and libraries. Each one in its own git repo.

Another Python example: Pydantic

The `dict` method is deprecated; use `model_dump` instead. Deprecated in Pydantic V2.0 to be removed in V3.0.

I like Pydantic, it is a very useful tool with great features. The 2.0 release also came with significant performance improvements. But this change doesn't make sense to me. I understand that it probably fits better within the domain of Pydantic itself.

Does this have to be a breaking change? I would suggest to have the both alternatives there. Yes, it might be a little bit more of maintenance for you as a library developer. More importantly: your users can focus on adding value into their products instead of this mostly zero-added-value work.

Example three: SQLAlchemy

MovedIn20Warning: The ``declarative_base()`` function is now available as sqlalchemy.orm.declarative_base(). (deprecated since: 2.0)

This is probably also correct, from an internal SQLAchemy domain perspective. From a user perspective, this only means that we need to change a lot of existing code. The cost of having the code overlapping in two namespaces is probably low.

I am also a maintainer of tools, and I've also made mistakes, or design choices that turned out to be not that great later on. But I also actively have made the decision to not force users to make the kind of changes that are described in this post. I don't want to break things for users of the tool because of design choices made in the past.

Should the users change their workflows?

The main thing I work on today is Python tooling for Polylith, that is a Monorepo architecture. The changes introduced by uv, Pydantic and SQLAlchemy actually isn't that much work for the developer teams using Polylith today. You'll have only one place in the source code where these changes are needed. This setup is robust, and is ready for any unexpected breaking changes in the tools that are used. Sounds nice, doesn't it?



Top Photo by generated by Dall-E, prompted and modified afterwards by me.

Sunday, April 20, 2025

Feedback loops in Python

How fast can we get useful feedback on the Python code we write?

This is a walk-through of some of the feedback loops we can get from our ways of working and developer tools. The goal of this post is to find a both Developer friendly and fast way to learn if the Python code we write actually does what we want it to.

What's a Feedback Loop?

From my point of view, feedback loops for software is about running code with optional input and verifying the output. Basically, run a Python function and investigate the result from that function.

Feedback Loop 1: Ship it to Production

Write some code & deploy it. When the code is up & running, you (or your customers) can verify if it works as expected or not. This is usually a very slow feedback loop cycle.

You might have some Continuous Integration (CI) already set up, with rules that should pass before the deployment. If your code doesn't pass the rules, the CI tool will let you know. As a feedback loop, it's slow. By slow, I mean that it takes a long time before you will know the result. Especially when there are setup & teardown steps happening in the CI process. As a guard, just before releasing code, CI with deployment rules is valuable and sometimes a life saver.

Commit, Push & Merge

Pull Requests: just before hitting the merge button, you will get a chance to review the code changes. This type of visual review is a manual feedback loop. It's good, because you often take a step back and reflect on the written code. Will the code do the thing right? Does the code do the right thing? One drawback is that you review all changes. For large Pull Requests, it can be overwhelming. From a feedback loop perspective, it's not that fast.

Testing and debugging

Obviously, this is a very common way for feedback on software. Either manual or automated. The manual is mostly a slower way to find out if the code does what expected or not, than an automated test. There's the integration-style automated tests, and the unit tests targeting the different parts. Integration-style tests often require mocking and more setup than unit tests. Both run fast, but the unit tests are more likely to be faster. You can have your development environment setup to automatically run the tests when something changes. Now we're getting close, this workflow can be fast.

I usually avoid the integration-type of tests, and rather write unit tests. I try to write small, focused and simple unit tests. The tests help me write small, focused and simple code too.

Test Driven Development

An even faster way to get feedback about the code is to write software in a test driven way (TDD): write a test that initially fails, write some code to make the test pass, refactor the test and refactor the code. For me, this workflow usually means jumping back-and-forth between the test and the code. Like a Ping Pong game.

TDD Deluxe

I'm not that strict about the TDD workflow. I don't always type the first lines of code in a test, or sometimes the test is halfway done when I begin to implement some of the code that should make the test pass. That's not pure TDD, I am aware. A few years ago, I found a new workflow that fits my sloppy approach very well. It's a thing called RDD (REPL Driven Development).

With RDD, you interactively write code. What does that even mean? For me, it's about writing small portions of code and evaluate it (i.e. run it) in the code editor. This gives me almost instant feedback on the code I just wrote. It's like the Ping Pong game with TDD, but even faster. Often, I also write inline code that later on evolves into a unit test. Adding some test data, evaluating a function with that test data, grab the response and assert it. The line between the code and the test is initially blurry, becoming clearer along the way. Should I keep the scratch-like code I wrote to evaluate a function? If yes, I have a unit test already. If not, I delete the code.

Interactive Python for fast Feedback Loops

I have written about the basic flows of REPL Driven Development before:

REPL - the Read Eval Print Feedback Loop

When starting a REPL session from within a virtual environment, you will have access to all the app-specific code. You can incrementally add code to the REPL session by importing modules, adding variables and functions. You can also redefine variables and functions within the session.

With REPL Driven Development, you have a running shell within your code editor. You mostly use the REPL shell to evaluate the code, not for writing code. You write the code as usual in your code editor, with the REPL/shell running there in the background. IPython is an essential tool for RDD in Python. It's configurable to auto-reload changed submodules, so you don't have to restart your REPL. Otherwise, it would have been very annoying.

Even more Interactive Python feedback loops

We can take this setup even further: modifying and evaluating an externally running Python program from your code editor. You can change the behavior of the program, without any app restarts, and check the current state of the app from within your IDE. The Are we there yet? post describes the main idea with this kind of setup and how I’ve configured my favorite code editor for it.

Jupyter, the Kernel and IPython

You might have heard of or already use Jupyter notebooks. To simplify, there's two parts involved: a kernel and a client. The Kernel is the Python environment. The client is the actual notebook. This type of setup can be used with REPL Driven Development too, having the code editor as the client and feeding the kernel or inspecting the current state by evaluating code. For this, we need a Kernel specification, a running Kernel, and we need to connect to the running Kernel from the IDE.

Creating a kernel specification

You can do this in several ways, but I find it most straightforward to add ipykernel as a dev dependency to the project.

    # Add the dependency (example using Poetry)
    poetry add ipykernel --group dev

    # generate the kernel specification
    python -m ipykernel install --user --name=the-python-project-name
    

The above commands will generate a kernel specification and is only run once. Now you have a ready-to-go kernel spec.

Start the Kernel
    jupyter kernel --kernel=the-python-project-name
    

The above command will start a kernel, using the specification we have generated. Please note the output from the command, with instructions on how to connect to it. Use the kernel path from the output to connect your client.

The tooling support I have added is as of this writing for Emacs. Have a look at this recording for a 13-minute demo on how to use this setup for a Fast & Developer Friendly Python Feedback Loop.



Top Photo by Timothy Dykes on Unsplash

Sunday, March 23, 2025

Are we there yet?

Continuing with the work on tooling support for interactive & fun development with Python.

A while ago, I wrote an article about my current attempts to make development in Python more interactive, more "test" driven and more fun.

My North Star is the developer experience in Clojure, where you have everything at your fingertips. Evaluating expressions (i.e. code) is a common thing for Lisp languages in general. I've found that it is sometimes difficult to explain this thing to fellow developers with no experience from Clojure development. The REPL is in most languages an external thing you do in a terminal window, detached from the work you usually do in your IDE. But that's not the case with REPL Driven Development (RDD).

Along the way, I have learned how to write and evaluate Python code within the code editor by using already existing tools and how to configure them. Here's my first post and second post (with setup guides) about it. You'll find information about the basic idea with RDD and guides on how to setup your IDE or code editor for Python development.

Can it be improved?

This has been the way I have done Python development most of the time, described in the posts above. But I have always wanted to find ways to improve this workflow, such as actually see the evaluated result in an overlay right next to the code. Not too long ago, I developed support for it in Emacs and it has worked really well! You can read about it and see examples of it here.

What about AI?

While writing Python code, and doing the RDD workflow, there's often a manual step to add some test or example data to variables and function input parameters. Recently, I got the idea to automate it using an LLM. I wrote a simple code editor command that prompts an AI to generate random (but relevant) values and then populate the variables with those. Nowadays, when I want to test Python code, I can prepare it with example data in a simple way by using a key combination. And then evaluate the code as before.

Are we there yet?

One important thing with RDD, that I haven't been able to figure out until now, is how to modify and evaluate code from an actual running program. This is how things are done in Clojure, you write code and have the running app, service, software constantly being changed while you develop it. Without any restarts. It is modified while it is running. Tools like NRepl does this in the background, with a client-server kind of architecture. I haven't dig that deep into how NRepl works, but believe it is similar to LSP (Language Server Protocol).

The workflow of changing a running program is really cool and something I've only seen before as a Clojure developer. So far I have used IPython as the underlying tool for REPL Driven Development (as described in the posts above).

A solution: the Kernel

In Python, we have something similar to NRepl: Jupyter. Many developers use Notebooks for interactive programming, and that is closely related to what I am trying to achieve. With a standard REPL session, you can add, remove and modify the Python code that lives in the session. That's great. But a sesssion is not the same as an actual running program.

A cool thing with Jupyter is that you can start a Python Kernel that clients can connect to. A client can be a shell, a Notebook - or a Code Editor.

jupyter kernel

By running the jupyter kernel command, you have a running Python program and can interactively add things to it, such as initiating and starting up a Python backend REST service. Being able to connect to the Jupyter kernel is very useful. While connected to the kernel, you can add, remove, modify the Python code - and the kernel will keep on running. This means that you can modify and test your REST service, without any restarts. With this, we are doing truly interactive Python programming. You will get instant feedback on the code you write, by evaluating the code in your editor or when testing the endpoint from a browser or shell.

"Dude, ever heard about debugging?"

Yes, of course. Debugging is closly related to this workflow, but it is not the same thing. Debugging is usually a one way flow. A timeline: you run code, pause the execution with breakpoints where you can inspect things, and then continue until the request is finalized. The RDD workflow, with modifying and evaluating a running program, doesn't have a one-way timeline. It's timeless. And you don't add breakpoints.

REPL Driven tooling support

I have developed tooling support for this new thing with connecting to a Jupyter Kernel, and so far it works well! Python is different from languages like Clojure: namespaces are relative to where the actual module is placed in the folder structure of a repo. This means that those connected to a Kernel need to have the full namespace (i.e. the full python path) for the items to inspect. This is what I have added in the tooling, so I can keep the RDD flow with the nice overlays and all.

I am an Emacs user, and the tooling I've written is for Emacs. But it shouldn't be too difficult to add it to your favorite code editor. Have a look at the code. You might even learn some Lisp along the way.


UPDATE: I have recorded a very much improvised video (with sound) explaining what is happening and how you start things up.

Resources

Top Photo by David Vujic

Friday, February 7, 2025

FOSDEM 25

FOSDEM, a Conference different from any other I've been to before. I'm very happy for the opportunity to talk, share knowledge and chat with the fellow devs there. I also met old and new friends in Brussels, and learned a little bit more about the nice Belgian beer culture. 😀

The FOSDEM event is free and you don't even register to attend (only speakers do that in a call for speakers process). Just come by the University Campus, located not far from the center of the Inner City.

I travelled towards Belgium with the Night Train from Stockholm, Sweden, and arrived the next morning in Hamburg, Germany. From there, I took the DB ICE Train to Köln/Cologne. At some point, the top speed was about 250 km/h. That's fast! From there, another fast train to Brussels.

The variety of topics and the amount of tracks at FOSDEM was mind blowing, with thousands of participants. The overall vibe was very friendly & laid back. I joined Rust focused talks, JavaScript and UX/Design talks. And, of course, the Python room talks.

My talk was in the Python room and was about Python Monorepos and the Polylith Developer Experience. Everything Python related was on FOSDEM Day 2 and I think my presentation went really well! There was a lot of questions afterwards and I got great feedback from the people attending.

The next day I went back the same route as before. I got a couple extra of hours to spend in Hamburg before onboarding the Night Train back home to Stockholm. A great Weekend Trip!

Here's the recording of my talk from FOSDEM 25:

The video was downloaded from fosdem.org.
Licensed under the Creative Commons Attribution 2.0 Belgium Licence.


Resources

Friday, January 3, 2025

Better Python Developer Productivity with RDD

"REPL Driven Development is an interactive development experience with fast feedback loops”

I have written about REPL Driven Development (RDD) before, and I use it in my daily workflow. You will find setups for Python development in Emacs, VS Code and PyCharm in this post from 2022. That's the setup I have used since then.

But there's one particular thing that I missed ever since I begun with RDD in Python. I learned this interactive REPL kind of writing code when developing services and apps with Clojure. When you evaluate code in Clojure - such as function calls or vars - the result of the evaluation nicely pops up as an overlay in your code editor, right next to the actual code. This is great, because that's also where the eyes are and what the brain is currently focused on.

The setup from my previous post will output the evaluated result in a separate window (or buffer, as it is called in Emacs). Still within the code editor, but in a separate view. That works well and has improved my Python Developing Productivity a lot, but I don't like the context switching. Can this Python workflow be improved?

Can I improve this somehow?

I've had that question in the back of my mind for quite some time. My Elisp skills are unfortunately not that great, and I think that has been a blocker for me. I've managed to write my own Emacs config, but that's about it. Until now. During the Christmas Holidays I decided to try learning some Emacs Lisp, to be able to develop something that reminds of the great experience from Clojure development.

I used an LLM find out how to do this, and along the way learned more about what's in the language. I'm not a heavy LLM/GPT/AI user at all. In fact, I rarely use it. But here it made sense to me, and I have used it to learn how to write and understand code in this particular language and environment.

I have done rewrites and a lot of refactoring of the result on my own (my future self will thank me). The code is refactored from a few, quite large and nested Lisp functions into several smaller and logically separated ones. Using an LLM to just get stuff done and move on without learning would be depressing. The same goes for copy-pasting code from StackOverflow, without reflecting on what the code actually does. Don't do that.

Ok, back to the RDD improvements. I can now do this, with new feature that is added to my code editor:

Selecting a variable, and inspecting what it contains by evaluating and displaying the result in an overlay.
Selecting a function, execute it and evaluate the result.

The overlay content is syntax highlighted as Python, and will be rendered into several rows when it's a lot of data.

The actual code evaluation is still performed in the in-editor IPython shell. But the result is extracted from the shell output, formatted and rendered as an overlay. I've chosen to also truncate the result if it's too large. The full result will still be printed in the Python shell anyway.
The Emacs Lisp code does this in steps:

  1. Adding a hook for a specific command (it's the elpy-shell-send-buffer-or-region command). The Emacs shortcut is C-c C-c.
  2. Capture the contents of the Python shell.
  3. Create an overlay with the evaluated result, based on the current cursor position.
  4. Remove the overlay when the cursor moves.

This is very much adapted to my current configuration, and I guess the real world testing of this new addition will be done after the holidays, starting next week. So far, so good!

Future improvements?

I'm currently looking into the possibilities of starting an external IPython or Jupyter/kernel session, and how to connect to it from within the code editor. I think that could enable even more REPL Driven Development productivity improvements.

You'll find the Emacs Lisp code at GitHub, in this repo, where I store my current Emacs setup.

Top Photo by Nicolai Berntsen on Unsplash

Sunday, December 22, 2024

Introducing python-hiccup

"All you need is list, set and dict"

Write HTML with Python

Python Hiccup is a library for representing HTML using plain Python data structures. It's a Python implementation of the Hiccup syntax.

You create HTML with Python, using list or tuple to represent HTML elements, and dict to represent the element attributes. The work on this library started out as a fun coding challenge, and now evolving into something useful for Python Dev teams.

Basic syntax

The first item in the list is the element. The rest is attributes, inner text or children. You can define nested structures or siblings by adding lists (or tuples if you prefer).

["div", "Hello world!"]
Using the html.render function of the library, the output will be HTML as a string: <div>Hello world!</div>

Adding id and classes:["div#foo.bar", "Hello world!"]
The HTML equivalent is: <div id="foo" class="bar">Hello world!</div>

If you prefer, you can define the attributes using a Python dict as an alternative to the compact syntax above: {"id": "foo", "class": "bar"}

Writing a nested HTML structure, using Python Hiccup:
["div", ["span", ["strong", "Hello world!"]]]
The HTML equivalent is:
<div><span><strong>Hello world!</strong></span></div>

Example usage

Server side rendering with FastAPI

Using the example from the FastAPI docs , but without the inline HTML. Instead, using the more compact and programmatic approach with the Python-friendly hiccup syntax.

from python_hiccup.html import render

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


def generate_html_response():
    data = ["html",
           ["head", ["title", "Some HTML in here"]],
           ["body", ["h1", "Look ma! HTML!"]]]

    return HTMLResponse(content=render(data), status_code=200)


@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return generate_html_response()

PyScript

Add python-hiccup as any other third-party dependency in the package.toml file: packages = ["python-hiccup"]

Write the HTML rendering in your PyScript files:

from pyweb import pydom
from python_hiccup.html import render

pydom["div#main"].html = render(["h1", "Look ma! HTML!"])

That's it!

python-hiccup aims to make HTML rendering programmatic, simple and readable. I hope you will find it useful. The HTML in this blog post was written using python-hiccup.

Resources



Top photo by Susan Holt Simpson on Unsplash

Tuesday, August 27, 2024

Simple Kubernetes in Python Monorepos

"Kubernetes, also known as K8s, is an open source system for automating deployment, scaling, and management of containerized applications."
(from kubernetes.io)

Setting up Kubenetes for a set of Microservices can be overwhelming at first sight.

I'm currently learning about K8s and the Ecosystem of tooling around it. One thing that I've found difficult is the actual K8s configuration and how the different parts relate to each other. The YAML syntax is readable, but I also find it hard to understand how to structure it - impossible to edit without having the documentation close at hand. When I first got the opportunity to work with Python code running in Kubernetes, I realized that I have to put extra effort in understanding what's going on in there.

I was a bit overwhelmed by what looked like a lot of repetitive and duplicated configuration. I don't like that. But there's a tool called Kustomize that can solve this, by incrementally constructing configuration objects with snippets of YAML.

Kustomize is about managing Kubernetes objects in a declarative way, by transforming a basic setup with environment-specific transformations. You can replace, merge or add parts of the configuration that is specific for the current environment. It reminds me of how we write reusable Python code in general. The latest version of the Kubernetes CLI - Kubectl - already includes Kustomize. It used to be a separate install.

Microservices

From my experience, the most common way of developing Microservices is to isolate the source code of each service in a separate git repository. Sometimes, shared code is extracted into libraries and put in separate repos. This way of working comes with tradeoffs. With the source code spread out in several repositories, there's a risk of having duplicated source code. Did I mention I don't like duplicated code?

Over time, it is likely that the services will run different versions of tools and dependencies, potentially also different Python versions. From a maintainability and code quality perspective, this can be a challenge.

YAML Duplication

In addition to having the Python code spread out in many repos, a common setup for Kubernetes is to do the same thing: having the service-specific configuration in the same repo as the service source code. I think it makes a lot of sense to have the K8s configuration close to the source code. But with the K8s configuration in separate repos, the tradeoffs are very much the same as for the Python source code.

For the YAML part in specific, it is even likely that the configuration will be duplicated many times across the repos. A lot of boilerplate configuration. This can lead to unnecessary extra work when needing to update something that affects many Microservices.

One solution to the tradeoffs with the source code and the Kubernetes configuration is: Monorepos.

K8s configuration in a Monorepo

A Monorepo is a repository containing source code and multiple deployable artifacts (or projects), i.e. a place where you would have all your Python code and where you would build & package several Microservices from. The purpose of a Monorepo is to simplify code reuse, and to use the same developer tooling setup for all code.

The Polylith Architecture is designed for this kind of workflow (I am the maintainer of the Python tools for the Polylith Architecture).

While learning, struggling and trying out K8s, I wanted to find ways to improve the Configuration Experience by applying the good ideas from the Developer Experience of Polylith. The goal is to make K8s configuration simple and joyful!

Local Development

You can try things out locally with developer tools like Minikube. With Minikube, you will have a local Kubernetes to experiment with, test configurations and to run your containerized microservices. It is possible to dry-run the commands or apply the setup into a local cluster, by using the K8s CLI with the Kustomize configs.

I have added examples of a reusable K8s configuration in the The Python Polylith Example repo. This Monorepo contains different types of projects, such as APIs and event handlers.

The K8s configuration is structured in three sections:

  • A basic setup for all types of deployments, i.e. the config that is common for all services.
  • Service-type specific setup (API and event handler specific)
  • Project-specific setup

All of theses sections have overlays for different environments (such as development, staging and production).

As an alternative, the project-specific configuration could also be placed in the top /kubernets folder.

I can run kubectl apply -k to deploy a project into the Minikube cluster, using the Kustomize configuration. Each section adds things to the configuration that is specific for the actual environment, the service type and the project.

The base, overlays and services are the parts that aren't aware of the project. Those Project-specific things are defined in the project section.

Using a structure like this will make the Kubernetes configuration reusable and with almost no duplications. Changing any common configuration only needs to be done in one place, just as with the Python code - the bricks - in a Polylith Monorepo.

That’s all. I hope you will find the ideas and examples in this post useful.


Resources


Top photo by Justus Menke on Unsplash