Last week, I got a LinkedIn message from a recruiter at a small crypto startup. We exchanged a few messages over a couple of days, she described a broken proof-of-concept they needed a lead engineer for, and then sent me a public GitHub repo to review. Specifically, she asked me to “check out the deprecated Node modules issue.” It’s not uncommon to ask for a review of an existing…
This is the sixth take on the same news reader. Previous versions used tool-calling agents. This time I used smolagents from HuggingFace, specifically its CodeAgent . Instead of calling predefined tools, the agent writes Python code and executes it in a sandbox. How CodeAgent works A CodeAgent follows a ReAct loop : think, act, observe, repeat. At each step, the LLM generates a Python snippet, the…
This is the fifth take on the same news reader. The Claude Agent SDK version was a single Python file with tools and sub-agents. The Pi SDK version was a single TypeScript file with an event-based session. This time I used Pydantic AI , the agent framework from the Pydantic team. Pydantic AI’s pitch is that since every AI library already depends on Pydantic for validation, you might as well…
In the previous post , I built a news reader using Pi’s CLI with an extension file and a system prompt file. Two files in a .pi/ directory, run with pi -p . It already felt minimal compared to the Claude Code version, but it still relied on Pi’s runtime to discover and load the extension. Pi also has a programmatic SDK that lets you embed the agent in your own script, the same way the…
I don’t know Pi well. I used it for a few small tasks and appreciated how minimal it felt. My news reader has become my test task for trying different agent tools against the same problem, so I rebuilt it with Pi to explore further. My opinions here may not survive deeper use, but the way I think about it: Claude Code is Django, Pi is Flask. Claude Code ships with WebFetch, WebSearch,…
In the previous post , I built a news reader that uses Claude Code as its execution environment. It worked well, but the project was spread across half a dozen config files, each in a different directory, connected by naming conventions. Changing anything meant knowing where each piece lived and how they were wired together. That post ended with a teaser about the Claude Agent SDK and a five-line…
I started with LLMs as another third-party dependency. In my apps, code defined the logic, and LLM calls handled small things like summarizing or tagging input. Over time, as agents gained more of my trust, I started experimenting with turning the execution model inside out. Instead of code that occasionally calls an LLM, I use the agent loop as the execution environment itself. The agent drives…
Committing .idea directory I never bothered committing .idea files before. Actually, I very much preferred treating it as a scary black box full of XML gibberish that I’d rather not touch. It was fine back in the days when I had a stable set of projects, each configured once in PyCharm, and that was it. Adopting git worktrees changed my workflow: every worktree is a new directory, and…
I give Claude Code longer and longer tasks. Agents got smarter, so I can set a task, switch to something else, and come back later. I set up a simple alarm with Claude Code hooks , shell commands that fire on lifecycle events, all calling the macOS say command: Here’s my ~/.claude/settings.json : { "hooks" : { "Stop" : [ { "hooks" : [ { "type" : "command" , "command" : "say -v Zarvox…
Good news, everyone. I invented a smell-o-scope. Smello captures outgoing HTTP requests from Python and displays them in a local web dashboard. It’s like Mailpit for HTTP. The whole setup is two lines of code: import smello smello . init() The Smello dashboard. Captured requests on the left, details on the right. My problem I work with external integrations a lot. Some of these APIs have…
Two independent things happened that drove me to reconsider my terminal workflow. First, I started relying more and more on Claude Code as my AI coding assistant running inside the terminal. Shift+Enter (for inserting newlines without submitting) didn’t work inside tmux, so I had to use a workaround. It was annoying, but acceptable. Then I switched to a 96-key keyboard with dual Bluetooth,…
Recently, I noticed a shift in how I use coding agents. I switched from Cursor to Claude Code, spending more time in the console than in an IDE. I started feeling comfortable offloading larger chunks of work to agents, steering them at a higher level and writing less code by hand. Simon Willison noticed something similar: the models got incrementally better, until GPT-5.2 and Opus 4.5 reached an…
In this post, I’m diving into what Cursor, the AI Code Editor, does behind the scenes when I ask it to write code for me. My main goal is to figure out how to craft my prompts for the best results and see if there’s a better way to tap into its workflow. It turns out that Cursor’s prompt structure reveals a lot about how it operates and how to set it up for maximum performance.
When working on external integrations, we often implement basic error handling. Most of the time, we just use resp.raise_for_status() and leave it for our future self to handle. Quite often, we don’t handle errors because we genuinely don’t know how the external system will behave and what types of errors to expect from it. Indeed, it can be overwhelming to consider all the possible corner cases…
To KISS or not to KISS I remember how, several years ago at Doist , we talked about integrating with external services, like email providers or analytics tools. The question was whether to use a simple function or an abstraction layer. We ended up having two groups. KISSers. The first one advocated for following the KISS principle and calling the service API directly. Something as simple as…
In update API endpoints that allow for partial updates (aka PATCH updates), we need to know if a model field has been explicitly set by the caller. Usually, we use None to indicate an unset value. But if None is also a valid value, how can we distinguish between None being explicitly set and None meaning “unset”? For example, let’s say we have an optional field in the user…
How to provide coding guidelines for GitHub Copilot and influence GitHub Copilot suggestions. In my project, I use naming conventions for models. I have different suffixes for different model types, and I subclass my models from various base classes. It would be awesome if I could let GitHub Copilot know about my conventions, so it can suggest more accurate code right from the start. Based on my…
Services are a missing layer in the Django architecture. Django does not provide guidance on where to place complex logic for modifying multiple models, but many development teams have chosen to follow the clean architecture pattern and introduce a service layer. This layer acts as a middleman between interfaces such as views or API endpoints, and models. However, in Django, many features are…
I created a Text Refiner , an open-source project that automatically fixes grammar and stylistic errors. It can also adjust the tone and formatting. That is addictively useful. Now, pretty much all my git commit messages, comments to Jira tickets, and Slack comments pass through the wisdom of this tool. The post you are reading has also gone through a series of refinements. How does it help me? It…
Photo by Valery Fedotov If you use Django class-based views (CBV), do you feel like it takes significant mental effort to wrap your head around the logic spread across various mixins? I have never been a big fan of Django’s class-based views (CBV) and their mixin approach. In my experience, classical function-based views (FBV) work as well as CBV, but provide more readable code. What are the…
Photo by Markus Winkler Parametrizing pytest fixtures is a powerful technique for writing more concise and readable tests. I find parametrizing fixtures helpful in two cases. When testing multiple implementations of the same interface. When testing the same function against different inputs. Testing interface implementations Thanks to Python duck typing, you don’t have to explicitly define…
Setting aside ten minutes daily to learn something new is possible. If it is not, where does your time go? On a personal level: as you wake up, you have roughly 100 such 10-minute blocks before you go to bed. Can you squeeze out one block before bed, while commuting, or after lunch? On a company level: your company is buying roughly 50 10-minute blocks per day from its employees. Investing one of…
When choosing between alternatives, whether big or small, it’s way too easy to fall into an analysis paralysis trap. Quite often, I find myself indecisive when I make a non-trivial purchase or a decision that will likely determine my future. In other words, not like choosing between types of cheese in a supermarket, but rather choosing which apartment to rent, which school to choose for my kids,…
Photo by Artem Bryzgalov If it hurts, do it more often In the summer of last year, I joined Building Radar as a contractor developer. The team recently launched a new product, onboarded some clients, and was looking for ways to move forward faster. When I joined, the team was releasing a new product version every three weeks, and the release process was somewhat painful. Things fell apart when…
There is a 2009 post from Derek Sivers, No yes. Either HELL YEAH! or no , a very short one, and it can be condensed even further into a single sentence. If you’re not saying “HELL YEAH!” about something, say no. Brilliant advice on learning to say “no” to so-so experiences to laser-focus on a single stellar thing. Yet, there is a big caveat! HELL YEAH doesn’t…
It’s been a little over a year since I read Atomic Habits and I wanted to share some of the key takeaways that have really resonated with me. When I first read the book, I thought it was an enjoyable read, but it didn’t necessarily blow my mind. It wasn’t until I started implementing some of the concepts from the book that I had my “a-ha” moment. Here are a few things that I’ve taken from the book…
Do you really need PostgreSQL enums? (Update from 9 Jan 2024) Working with Django reminds me how much less painful enums could be if they were represented by VARCHAR fields at the database level and enforced by Django models at the application level. Inspired by this, in my next SQLAlchemy model, I used the ChoiceType from the sqlalchemy-utils package. Actually, I combined it with Tiangolo’s…
What I Wrote About in 2022 In 2022, I wrote 20 blog posts starting with Dealing with large pull requests and closing the year with Stop Me Before It’s Too Late . Additionally, I tried to record my TIL findings in May and June, but quickly abandoned this idea after only three posts, still trying to determine if the format made sense. The topics of my blog posts ranged from niche technical…
The New Year celebration is tough for me. One thing that makes it especially challenging is that the tradition requires you to be awake enough to observe the second 00:00:01 of the upcoming year and enthusiastically cheer its arrival. Throughout the years, I’ve learned that while I can be awake, there is never enough energy to be enthusiastic about the event. Starting at around 10 PM, I get…
Proactivity and communication are essential for healthy relationships at work. I often use the phrase “Stop me before it’s too late” in my work style and find that it can serve as a good catchphrase marker for these two traits. The context of saying “stop me before it’s too late” is typically when I encounter a problem (usually a blocker) with a non-trivial…
Data structures evolve with time. Say you have data storage with person objects. I assume the storage is schema-less. Maybe it’s your primary storage in MongoDB, a Redis cache, or a log record in ElasticSearch. { "name" : "Guido Van Rossum" , "email" : "guido@python.org" } At some point, you decide to store a profession alongside each person. New records can look like this. { "name" : "Ryan Dahl"…
You write a specification draft and share it with colleagues for feedback. With Google Docs, Notion, or Confluence, typically, colleagues leave contextual feedback on specific lines of the document. From there, an entire discussion can evolve. A specification with many unresolved comments is a time sink for everyone who touches it. Not only do they need to read the spec, they also need to follow…
Martin Fowler argues that functions should be as short as a few lines of code. I feel like that’s a bit extreme, and usually, my functions are longer than that. It turns out my test functions are a different beast. Reflecting on how I write code, I noticed that my test functions are usually much shorter than my regular code. Not only are they shorter, but in general, they look different.
I passed the AWS Certified Developer - Associate certification exam a week ago. Below I’m sharing my journey, thoughts, and tips about the process. Why did I decide to pass the certification exam? In broad strokes, I had two primary goals: the certificate and the knowledge that the preparation brings. Knowledge part. I worked with AWS for many years and felt I understood the ecosystem well…
You know developers solve business problems with code. Taking this literally, you are so busy working on new features that you often find yourself taking implementation shortcuts. Under the pressure of the product team, you don’t have time to go back and clean it up. You know, in the long run, this is not sustainable. Code shortcuts inevitably make product quality degrade. Still, while…
Performance profiling in Python When it comes to performance, you never know where the bottleneck is. Fortunately, the Python ecosystem has tools to eliminate guesswork. They are easy to learn and don’t require codebase instrumentation or preparatory work. When I worked at Doist, I spent hours staring at the profiling plots of our full sync API. In Todoist API , full sync is the first…
I started my new project with the AWS SAM framework . In this post, I share some takeaways I have learned about creating serverless applications with it. Learning curve Overall, it’s a different experience from writing a web app built around a classical monolith web framework. If you look at speakers presenting AWS SAM on stage, you may feel that it’s an excellent choice for a quick…
A memo about my setup for a TypeScript React project: I copy and paste these commands whenever I need to start a new project and ensure that it has all linters, formatters, and pre-commit hooks configured. Initialize proper version I use nvm , and I automatically initialize the node environment with nvm if .nvmrc is found. # File ~/.zshrc test -f ./.nvmrc && nvm use Start a new project I tried…
$100 per year for GitHub Copilot is a no-brainer investment. The expectations from a tool that writes code are as inflated as programmers’ salaries and egos, but Copilot delivers on them. It helps me switch contexts less often and stay in the flow. It saves my mental energy by writing boilerplate code, helping me come up with consistent variable names, and writing complete documentation.…
Photo by Elisa Ventur I started my career as a developer, then spent several years as a team lead, and then got back to mostly writing code again. Here, I’m reflecting on the challenges of being a middle manager. Everything is more complicated than it looks. Why is my manager so stressed? They may not know how to manage yet All the time, brilliant individual contributors get promoted to team…
React ↔︎ FastAPI dashboard I work on a dashboard that shows charts with filters. React serializes filter parameters into JSON and sends them to FastAPI in a query string. The FastAPI server returns the data to display the chart. My dashboard FastAPI parses your request and maps it to controller parameters. Can I turn complex parameters in the query string into Pydantic models? class Quarter…
Stray service I got a surprise bill from AWS. More than $300 for May and almost $100 for June. Until then, I thought that surprise bills were something that happened to others. My surprise bill from AWS About a month ago, I completed the Amazon MQ workshop . I was sure that I destroyed all the services when I completed it. It turns out that was not the case. The CloudFormation template failed to…
I came across the state of JavaScript 2021 report, released in February. The report falls into the same bucket as the Python Developer Survey and StackOverflow developer survey , but with focus on the JavaScript ecosystem. I found it to be full of insights. Many features of modern JavaScript are not known to me. As someone who considers himself “good enough” in JavaScript, I find it…
How it works Data is stored in tables with items like rows. Rows in tables are uniquely identified by their primary key, consisting of a partition key and an optional sort key. The schema is not defined for the rest of the attributes. Different items can have different attributes or different types of the same attribute. Sharding is automated by the hash of the partition key. Data consistency…
Photo by Markus Spiske I could never expect to read an anti-utopia not as a grim overview of the future but as an outline of today’s world. I was reading this to reflect on the current events in Russia and thinking about why so many people fell victim to relatively unsophisticated propaganda and became its supporters and apologists. My biggest takeaway is how amazingly adaptive the human…
Played with Elastic Beanstalk . At first glance, it looks like a very user-unfriendly version of Heroku. At a second glance, better than it seems. They offer out-of-the-box autoscaling, monitoring, spot instances, and even a wrapper around SQS for queues. The admin panel is quite user-friendly by AWS standards. In general, of course, everything smells antiquity. There have been no notable updates…
I released Django Plausible Proxy , a Django application to proxy requests and send server-side events to Plausible Analytics . Plausible is a lightweight and open-source web analytics platform, a privacy-friendly alternative to Google Analytics. I started using it for Django side projects about a month ago and loved its minimalistic interface and “exactly what I need” type of reports.…
In software development, we often make decisions that look like an excellent idea in the short run but result in the horror of maintenance down the road. Letting two independent services use a shared database is one of those ideas. More often, I’ve seen a less obvious variant. There’s a legacy system that’s hard to maintain. When the business needs new functionality quickly, they…
Photo by Jess Zoerb Repaying your tech debt with a code cleanup hackathon? Planning a bug fixing week to decrease the issues backlog? Likely, you’re wasting your time. When I worked at Doist, we had a long-standing issue with Sentry. Sentry is an error tracking service, and our application recorded every exception there. Quickly, the list of issues grew so deep that our team learned to…
In a post Don’t let dicts spoil your code I wrote that it’s better to avoid raw data structures such as dicts and lists. Instead, I suggest converting them as soon as possible to objects representing your domain. In a few places in my code, I found that raw dicts appear as attributes of SQLAlchemy models’ JSON fields. It feels dirty: you have a well-defined model with a field…