Feed: Roman Imankulov

Entries found: 71

Cursor Under the Hood

Published: Sun, 23 Feb 2025 00:00:00 +0000
Updated: Sun, 23 Feb 2025 00:00:00 +0000
UTC: 2025-02-23 00:00:00+00:00
URL: https://roman.pt/posts/cursor-under-the-hood/

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.
Content Preview

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.

Temporary vs Permanent Errors

Published: Sun, 05 May 2024 00:00:00 +0000
Updated: Sun, 05 May 2024 00:00:00 +0000
UTC: 2024-05-05 00:00:00+00:00
URL: https://roman.pt/posts/temporary-vs-permanent-errors/

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 your 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 and provide appropriate reactions to them. What should I do if the server returns a 503 error? What if I am rate-limited? What if there’s a connection timeout, and so on? It involves a long list of exceptional cases and handlers that need to be implemented, documented, and tested.
Content Preview
Temporary vs Permanent Errors

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 your 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 and provide appropriate reactions to them. What should I do if the server returns a 503 error? What if I am rate-limited? What if there’s a connection timeout, and so on? It involves a long list of exceptional cases and handlers that need to be implemented, documented, and tested.

Interface-mock-live (IML) pattern for connecting with third-party services in Python applications

Published: Mon, 08 Jan 2024 00:00:00 +0000
Updated: Mon, 08 Jan 2024 00:00:00 +0000
UTC: 2024-01-08 00:00:00+00:00
URL: https://roman.pt/posts/iml/

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 with having two groups. KISSers. The first one advocated for following the KISS principle and calling the service API directly. Something as simple as storing an API token in your settings, then calling requests.get() on an endpoint. Wrap this in a function you’re done.
Content Preview

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 with having two groups.

  • KISSers. The first one advocated for following the KISS principle and calling the service API directly. Something as simple as storing an API token in your settings, then calling requests.get() on an endpoint. Wrap this in a function you’re done.

Handling Unset Values in Fastapi With Pydantic

Published: Tue, 26 Dec 2023 00:00:00 +0000
Updated: Tue, 26 Dec 2023 00:00:00 +0000
UTC: 2023-12-26 00:00:00+00:00
URL: https://roman.pt/posts/handling-unset-values-in-fastapi-with-pydantic/

In the 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 profile, like a phone number. When using the user profile API endpoint, it’s not clear what to do if the phone number value is None. Should we reset it or keep it as it is?
Content Preview

In the 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 profile, like a phone number. When using the user profile API endpoint, it’s not clear what to do if the phone number value is None. Should we reset it or keep it as it is?

GitHub Copilot Context

Published: Tue, 19 Dec 2023 00:00:00 +0000
Updated: Tue, 19 Dec 2023 00:00:00 +0000
UTC: 2023-12-19 00:00:00+00:00
URL: https://roman.pt/posts/copilot-context/

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 experience, GitHub Copilot seems to make suggestions by considering the context beyond the current file. The developers of Copilot themselves have confirmed this, although they haven’t explicitly mentioned what exactly is included in this context. Here’s a discussion on this topic: GitHub Copilot Context Discussion.
Content Preview

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 experience, GitHub Copilot seems to make suggestions by considering the context beyond the current file. The developers of Copilot themselves have confirmed this, although they haven’t explicitly mentioned what exactly is included in this context. Here’s a discussion on this topic: GitHub Copilot Context Discussion .

Django Admin and Service Layer

Published: Tue, 15 Aug 2023 00:00:00 +0000
Updated: Tue, 15 Aug 2023 00:00:00 +0000
UTC: 2023-08-15 00:00:00+00:00
URL: https://roman.pt/posts/django-admin-and-service-layer/

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 built around direct use of models. For example, Django admin assumes that models are directly modified and calls the save() method on changes.
Content Preview

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 built around direct use of models. For example, Django admin assumes that models are directly modified and calls the save() method on changes.

Refiner

Published: Tue, 11 Jul 2023 00:00:00 +0000
Updated: Tue, 11 Jul 2023 00:00:00 +0000
UTC: 2023-07-11 00:00:00+00:00
URL: https://roman.pt/posts/refiner/

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 helps minimize any non-native speaker quirks. Sometimes, I struggle with complex grammar constructions or end up using expressions that don’t quite exist. I’m sure my English writing has a noticeable Russian accent, even if I can’t hear it. The refiner helps me make my writing sound more natural to native speakers.
Content Preview
Refiner

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 helps minimize any non-native speaker quirks. Sometimes, I struggle with complex grammar constructions or end up using expressions that don’t quite exist. I’m sure my English writing has a noticeable Russian accent, even if I can’t hear it. The refiner helps me make my writing sound more natural to native speakers.

From Django class-based views to service functions

Published: Thu, 06 Apr 2023 00:00:00 +0000
Updated: Thu, 06 Apr 2023 00:00:00 +0000
UTC: 2023-04-06 00:00:00+00:00
URL: https://roman.pt/posts/django-views-and-service-functions/

Photo by Valery Fedotov If you use Django class-based views (CBV), do you feel like it takes significant mental effort to wrap your hand 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 good as CBV, but provide more readable code. What are the problems of class-based views The logic stops being linear. Instead of running the code sequentially as written, the execution flow jumps between different small functions and classes in seemingly random order. Some classes belong to your code, while others are provided by the framework. Some methods extend methods of superclasses and call super() forcing you to go down deeper the rabbit hole. Serving a noble purpose of code de-duplication, class-based views often offer the cure that is worse than the disease. The code gets messy in an attempt to save or reuse a few lines of code.
Content Preview
Django Views and Service Functions
Photo by Valery Fedotov

If you use Django class-based views (CBV), do you feel like it takes significant mental effort to wrap your hand 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 good as CBV, but provide more readable code.

What are the problems of class-based views

The logic stops being linear . Instead of running the code sequentially as written, the execution flow jumps between different small functions and classes in seemingly random order. Some classes belong to your code, while others are provided by the framework. Some methods extend methods of superclasses and call super() forcing you to go down deeper the rabbit hole. Serving a noble purpose of code de-duplication, class-based views often offer the cure that is worse than the disease. The code gets messy in an attempt to save or reuse a few lines of code.

Parametrizing Pytest Fixtures

Published: Tue, 07 Mar 2023 00:00:00 +0000
Updated: Tue, 07 Mar 2023 00:00:00 +0000
UTC: 2023-03-07 00:00:00+00:00
URL: https://roman.pt/posts/parametrizing-pytest-fixtures/

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 interfaces. Any object that has the required attributes and methods can be used as an interface implementation.
Content Preview
Parametrizing Pytest Fixtures
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 interfaces. Any object that has the required attributes and methods can be used as an interface implementation.

10 Minutes

Published: Tue, 28 Feb 2023 00:00:00 +0000
Updated: Tue, 28 Feb 2023 00:00:00 +0000
UTC: 2023-02-28 00:00:00+00:00
URL: https://roman.pt/posts/10-minutes/

Photo by Lukas Blazek Setting aside ten minutes daily to learn something 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, 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 those fifty blocks in learning doesn’t make a difference for day-to-day work but sets the foundation for the individual growth of your team members.
Content Preview
Everyone has ten minutes to learn something new
Photo by Lukas Blazek

Setting aside ten minutes daily to learn something 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, 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 those fifty blocks in learning doesn’t make a difference for day-to-day work but sets the foundation for the individual growth of your team members.

How I Make Choices

Published: Tue, 21 Feb 2023 00:00:00 +0000
Updated: Tue, 21 Feb 2023 00:00:00 +0000
UTC: 2023-02-21 00:00:00+00:00
URL: https://roman.pt/posts/how-i-make-choices/

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 solution that will likely determine my future. In other words, not like choosing between sorts of cheese in a supermarket, but rather choosing which apartment to rent, which school to choose for my kids, where country to live, which framework to use for my next side-project, etc.
Content Preview
How I Make Choices

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 solution that will likely determine my future. In other words, not like choosing between sorts of cheese in a supermarket, but rather choosing which apartment to rent, which school to choose for my kids, where country to live, which framework to use for my next side-project, etc.

Feature Flags at Building Radar

Published: Tue, 14 Feb 2023 00:00:00 +0000
Updated: Tue, 14 Feb 2023 00:00:00 +0000
UTC: 2023-02-14 00:00:00+00:00
URL: https://roman.pt/posts/feature-flags-at-building-radar/

Photo by Artem Bryzgalov If it hurts, do it more often In the summer of the past 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 deployed to production, migrations took longer than anticipated, features didn’t work quite as advertised, etc.
Content Preview
Feature Flags at Building Radar
Photo by Artem Bryzgalov

If it hurts, do it more often

In the summer of the past 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 deployed to production, migrations took longer than anticipated, features didn’t work quite as advertised, etc.

Say Maybe to Hell Yeah

Published: Tue, 31 Jan 2023 00:00:00 +0000
Updated: Tue, 31 Jan 2023 00:00:00 +0000
UTC: 2023-01-31 00:00:00+00:00
URL: https://roman.pt/posts/say-maybe-to-hell-yeah/

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 in 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-big caveat! HELL YEAH doesn’t guarantee you make the best choice out of the outcomes. It just helps you make the choice you are most excited about right now. And it’s not the same thing.
Content Preview

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 in 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-big caveat! HELL YEAH doesn’t guarantee you make the best choice out of the outcomes. It just helps you make the choice you are most excited about right now. And it’s not the same thing.

7 habit-building techniques I learned from 'Atomic Habits'

Published: Tue, 24 Jan 2023 00:00:00 +0000
Updated: Tue, 24 Jan 2023 00:00:00 +0000
UTC: 2023-01-24 00:00:00+00:00
URL: https://roman.pt/posts/atomic-habits/

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 and how they’ve helped me:
Content Preview

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 and how they’ve helped me:

Simplifying PostgreSQL enum migrations with SQLAlchemy and alembic-enums

Published: Tue, 17 Jan 2023 00:00:00 +0000
Updated: Tue, 17 Jan 2023 00:00:00 +0000
UTC: 2023-01-17 00:00:00+00:00
URL: https://roman.pt/posts/alembic-enums/

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 SQLModel, and the development experience was quite nice.
Content Preview
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 SQLModel, and the development experience was quite nice.

Most Popular Posts of 2022

Published: Tue, 10 Jan 2023 00:00:00 +0000
Updated: Tue, 10 Jan 2023 00:00:00 +0000
UTC: 2023-01-10 00:00:00+00:00
URL: https://roman.pt/posts/most-popular-posts-of-2022/

What I wrote about in 2022? In 2022, I wrote 20 blog posts starting from a 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 subjects like Parse JSON-encoded query strings in FastAPI, to thoughts on management and teamwork like Be kind to your manager, to classic rants like AWS Surprise Bill and musings on society and the dark sides of human nature in 1984 is now.
Content Preview

What I wrote about in 2022?

In 2022, I wrote 20 blog posts starting from a 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 subjects like Parse JSON-encoded query strings in FastAPI , to thoughts on management and teamwork like Be kind to your manager , to classic rants like AWS Surprise Bill and musings on society and the dark sides of human nature in 1984 is now .

Surviving Late Night Events

Published: Sun, 01 Jan 2023 11:00:00 +0000
Updated: Sun, 01 Jan 2023 11:00:00 +0000
UTC: 2023-01-01 11:00:00+00:00
URL: https://roman.pt/posts/surviving-late-night-events/

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 more tired and grumpier with every minute. I talk less, and my remarks usually become more caustic and socially inappropriate when I do decide to open my mouth.
Content Preview

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 more tired and grumpier with every minute. I talk less, and my remarks usually become more caustic and socially inappropriate when I do decide to open my mouth.

Stop Me Before It's Too Late

Published: Tue, 13 Dec 2022 00:00:00 +0000
Updated: Tue, 13 Dec 2022 00:00:00 +0000
UTC: 2022-12-13 00:00:00+00:00
URL: https://roman.pt/posts/stop-me-before-its-too-late/

Proactivity and communication are essential for healthy relationships at work. I often use the phrase “Stop me before it’s too late” in this 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 solution, and I’m about to fix it using my best judgment. Before starting, I usually communicate the problem and my proposed solution in writing and invite feedback from my colleagues. I give them the opportunity to stop me before it gets too late.
Content Preview

Proactivity and communication are essential for healthy relationships at work. I often use the phrase “Stop me before it’s too late” in this 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 solution, and I’m about to fix it using my best judgment. Before starting, I usually communicate the problem and my proposed solution in writing and invite feedback from my colleagues. I give them the opportunity to stop me before it gets too late.

Pydantic as a Backward Compatibility Layer

Published: Tue, 06 Dec 2022 00:00:00 +0000
Updated: Tue, 06 Dec 2022 00:00:00 +0000
UTC: 2022-12-06 00:00:00+00:00
URL: https://roman.pt/posts/pydantic-as-backward-compatibility-layer/

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", "email": "ryan@nodejs.com", "profession": "Software developer" } Old objects stored in the database before the migration don’t have a “profession” attribute. If your code expects every person to have a defined profession field, it will crash the first time it interacts with the old code.
Content Preview
Pydantic as Backward Compatibility Layer

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",
 "email": "ryan@nodejs.com",
 "profession": "Software developer"
}

Old objects stored in the database before the migration don’t have a “profession” attribute. If your code expects every person to have a defined profession field, it will crash the first time it interacts with the old code.

Resolve Discussions

Published: Tue, 30 Aug 2022 08:00:00 +0100
Updated: Tue, 30 Aug 2022 08:00:00 +0100
UTC: 2022-08-30 07:00:00+00:00
URL: https://roman.pt/posts/resolve-discussions/

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 all the threads and discussions and find the final opinion. It’s not uncommon for some of them to hang without the resolution, leaving it to whoever made the implementation.
Content Preview

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 all the threads and discussions and find the final opinion. It’s not uncommon for some of them to hang without the resolution, leaving it to whoever made the implementation.

Long name, short body

Published: Tue, 16 Aug 2022 00:00:00 +0000
Updated: Tue, 16 Aug 2022 00:00:00 +0000
UTC: 2022-08-16 00:00:00+00:00
URL: https://roman.pt/posts/long-name-short-body/

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.
Content Preview

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.

AWS Certified Developer

Published: Tue, 02 Aug 2022 00:00:00 +0000
Updated: Tue, 02 Aug 2022 00:00:00 +0000
UTC: 2022-08-02 00:00:00+00:00
URL: https://roman.pt/posts/aws-certified-developer/

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 I decided 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 enough. Still, I always had a feeling that my knowledge was quite chaotic and, likely, incomplete. I wanted to have a more systematic approach to learning about the platform.
Content Preview
AWS Certified Developer - Associate certificate

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 I decided 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 enough. Still, I always had a feeling that my knowledge was quite chaotic and, likely, incomplete. I wanted to have a more systematic approach to learning about the platform.

Opportunistic Refactoring

Published: Tue, 26 Jul 2022 00:00:00 +0000
Updated: Tue, 26 Jul 2022 00:00:00 +0000
UTC: 2022-07-26 00:00:00+00:00
URL: https://roman.pt/posts/opportunistic-refactoring/

You know developers solve business problems with code. Taking this literally, you are so busy working on new features that you often find yourself making implementation shortcuts. Under the pressure of the product team, you don’t have time to get back and clean it up. You know, in the long run, this is not sustainable. Shortcuts of the code inevitably make product quality degrade. Still, while evident in hindsight, the idea is hard to sell to product folks.
Content Preview

You know developers solve business problems with code. Taking this literally, you are so busy working on new features that you often find yourself making implementation shortcuts. Under the pressure of the product team, you don’t have time to get back and clean it up.

You know, in the long run, this is not sustainable. Shortcuts of the code inevitably make product quality degrade. Still, while evident in hindsight, the idea is hard to sell to product folks.

Python Performance Profiling

Published: Tue, 19 Jul 2022 00:00:00 +0000
Updated: Tue, 19 Jul 2022 00:00:00 +0000
UTC: 2022-07-19 00:00:00+00:00
URL: https://roman.pt/posts/python-performance-profiling/

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 command called after a login. In our battle for a smooth experience, we went to great lengths to optimize the first load.
Content Preview

Performance profiling in Python

Speedometer

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 command called after a login. In our battle for a smooth experience, we went to great lengths to optimize the first load.

AWS SAM

Published: Tue, 12 Jul 2022 00:00:00 +0000
Updated: Tue, 12 Jul 2022 00:00:00 +0000
UTC: 2022-07-12 00:00:00+00:00
URL: https://roman.pt/posts/aws-sam/

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, 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 prototype or an API endpoint that you can start and finish within a few hours. In reality, if you don’t have much experience building cloud-native applications, there is a lot to learn, mostly with trial and error.
Content Preview

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, 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 prototype or an API endpoint that you can start and finish within a few hours. In reality, if you don’t have much experience building cloud-native applications, there is a lot to learn, mostly with trial and error.

My setup for a typescript react project

Published: Tue, 05 Jul 2022 00:00:00 +0000
Updated: Tue, 05 Jul 2022 00:00:00 +0000
UTC: 2022-07-05 00:00:00+00:00
URL: https://roman.pt/posts/typescript-react-project-setup/

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 Vite for a few recent projects and liked it. That’s how I create a new empty project.
Content Preview

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 Vite for a few recent projects and liked it. That’s how I create a new empty project.

GitHub Copilot

Published: Tue, 28 Jun 2022 00:00:00 +0000
Updated: Tue, 28 Jun 2022 00:00:00 +0000
UTC: 2022-06-28 00:00:00+00:00
URL: https://roman.pt/posts/github-copilot/

$100 per year for a GitHub copilot is a no-brainer investment. The expectations from a tool that writes code are as inflated as programmers’ salaries and ego, but Copilot delivers to 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 variables names, and writing complete documentation. Conservatively, even if it saves me five minutes daily, it’s still a good investment. Realistically, the amount of saved time can get up to an hour on a good day.
Content Preview

$100 per year for a GitHub copilot is a no-brainer investment. The expectations from a tool that writes code are as inflated as programmers’ salaries and ego, but Copilot delivers to 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 variables names, and writing complete documentation.

Conservatively, even if it saves me five minutes daily, it’s still a good investment. Realistically, the amount of saved time can get up to an hour on a good day.

Be kind to your manager

Published: Tue, 21 Jun 2022 00:00:00 +0000
Updated: Tue, 21 Jun 2022 00:00:00 +0000
UTC: 2022-06-21 00:00:00+00:00
URL: https://roman.pt/posts/be-kind-to-your-manager/

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 my manager is so stressed? They may not know how to manage yet All the time, brilliant individual contributors got promoted to team leads only to learn that that’s a job requiring a completely different skill set.
Content Preview
Be Kind to Your Manager
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 my manager is so stressed?

They may not know how to manage yet

All the time, brilliant individual contributors got promoted to team leads only to learn that that’s a job requiring a completely different skill set.

Parse JSON-encoded query strings in FastAPI

Published: Tue, 14 Jun 2022 04:00:00 +0000
Updated: Tue, 14 Jun 2022 04:00:00 +0000
UTC: 2022-06-14 04:00:00+00:00
URL: https://roman.pt/posts/fastapi-json-query/

React ↔︎ FastAPI dashboard I work on a dashboard that shows charts with filters. React serializes filter parameters into JSON and sends them to the 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(BaseModel): q: int year: int @app.get("/api/v1/chart") def get_chart( start_quarter: Quarter = parse_querystring_and_create_quarter_instance_somehow(), end_quarter: Quarter = parse_querystring_and_create_quarter_instance_somehow(), ): ... FastAPI request mapping FastAPI automatically maps query strings to scalar function parameters. We get strings inside the functions.
Content Preview

React ↔︎ FastAPI dashboard

I work on a dashboard that shows charts with filters.

  • React serializes filter parameters into JSON and sends them to the FastAPI in a query string.
  • The FastAPI server returns the data to display the chart.
My dashboard

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(BaseModel):
 q: int
 year: int


@app.get("/api/v1/chart")
def get_chart(
 start_quarter: Quarter = parse_querystring_and_create_quarter_instance_somehow(),
 end_quarter: Quarter = parse_querystring_and_create_quarter_instance_somehow(),
):
 ...

FastAPI request mapping

FastAPI automatically maps query strings to scalar function parameters. We get strings inside the functions.

AWS Surprise Bill

Published: Tue, 07 Jun 2022 09:25:15 +0100
Updated: Tue, 07 Jun 2022 09:25:15 +0100
UTC: 2022-06-07 08:25:15+00:00
URL: https://roman.pt/posts/aws-surprise-bill/

Stray service I got a surprise bill from AWS. More than $300 for May and almost $100 for June. Until that, I thought that surprise bills were something that happened to others. My surprise bill from AWS About a month ago, I passed the Amazon MQ workshop. I was sure that I destroyed all the services as I completed it. It turns out that it was not the case. The CloudFormation template failed to delete the Amazon MQ broker, and I overlooked that error.
Content Preview

Stray service

I got a surprise bill from AWS. More than $300 for May and almost $100 for June. Until that, I thought that surprise bills were something that happened to others.

My surprise bill from AWS

My surprise bill from AWS

About a month ago, I passed the Amazon MQ workshop . I was sure that I destroyed all the services as I completed it. It turns out that it was not the case. The CloudFormation template failed to delete the Amazon MQ broker, and I overlooked that error.

State of JS 2021

Published: Wed, 01 Jun 2022 00:00:00 +0000
Updated: Wed, 01 Jun 2022 00:00:00 +0000
UTC: 2022-06-01 00:00:00+00:00
URL: https://roman.pt/til/2022-06-01/

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.
Content Preview

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 both exciting and uncomfortable: oh, the number of things I still don’t know is enormous.
  • It was interesting to look at the top list of the frameworks. With React as the most popular, Svelte as something people want to learn, and Solid (never heard of it) which brings the most satisfaction.
  • Vite , a better webpack, is on the top list of interest and satisfaction. This observation matches my experience. I started my new project with Vite, React, and TypeScript ( yarn create vite --template react-ts ), and I can confirm that it does everything necessary out of the box, and so far, I have never had any problems with it.
  • I liked the “Resources” review that features blogs, podcasts, and people to follow in the JavaScript community.
  • Bookmarked nvio.rocks for React charts.

DynamoDB

Published: Fri, 27 May 2022 00:00:00 +0000
Updated: Fri, 27 May 2022 00:00:00 +0000
UTC: 2022-05-27 00:00:00+00:00
URL: https://roman.pt/til/2022-05-27/

LSI shares partitions with the data and has the same partition key but a different key. Of limited use.
Content Preview

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

  • Eventual consistency is the default behavior. Strong consistency is an option. Strongly consistent reads are more expensive (2x), and sometimes they may not be available.
  • Advice: try to design around eventual consistency.

Throughput

  • Measured in RCU (read capacity units) and WCU (write capacity units)
  • 1 RCU - one strongly or two eventually consistent reads per second (at most 4kb)
  • 1 WCU - one standard write per second (at most 1kb)
  • Tables are created with the minimal provisioned capacity of 1 RCU and 1 WCU, which costs around $0.59 per month. Can scale up to 10 RCU or 10 WCU.
  • Throttling on exceeding.

Querying data

  • Scan. Sort of like SCAN in Redis. Iterates over all the records in the table and returns only the ones matching the criteria. Avoid this.
  • Query. Specify the partition and the sort key expression. It’s cheaper and faster.

Local secondary indexes (LSI)

LSI shares partitions with the data and has the same partition key but a different key. Of limited use.

1984 is now

Published: Mon, 16 May 2022 14:46:21 +0100
Updated: Mon, 16 May 2022 14:46:21 +0100
UTC: 2022-05-16 13:46:21+00:00
URL: https://roman.pt/posts/1984/

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 why so many people fell victim to relatively unsophisticated propaganda and became their supporters and apologists. My biggest takeaway is how amazingly adaptive the human mind is. People can go to great lengths to deny the most apparent facts to protect their safety and comfort.
Content Preview
1984 is now
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 why so many people fell victim to relatively unsophisticated propaganda and became their supporters and apologists.

My biggest takeaway is how amazingly adaptive the human mind is. People can go to great lengths to deny the most apparent facts to protect their safety and comfort.

Elastic Beanstalk

Published: Wed, 11 May 2022 00:00:00 +0000
Updated: Wed, 11 May 2022 00:00:00 +0000
UTC: 2022-05-11 00:00:00+00:00
URL: https://roman.pt/til/2022-05-11/

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 lately. The latest version of python is 3.8. In 2011 when they announced it, it could have become a hit, but it didn’t because there were no RDS at the time. When RDS came in, the platform seemed to be of no use to anyone.
Content Preview

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 lately. The latest version of python is 3.8.

In 2011 when they announced it, it could have become a hit, but it didn’t because there were no RDS at the time. When RDS came in, the platform seemed to be of no use to anyone.

Django Plausible Proxy

Published: Wed, 27 Apr 2022 00:00:00 +0000
Updated: Wed, 27 Apr 2022 00:00:00 +0000
UTC: 2022-04-27 00:00:00+00:00
URL: https://roman.pt/posts/django-plausible-proxy/

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. Initially, the integration code lived inside the project, but as it grew in functionality, I extracted it in a separate package that is available on PyPI and GitHub.
Content Preview
Django Plausible Proxy

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.

Initially, the integration code lived inside the project, but as it grew in functionality, I extracted it in a separate package that is available on PyPI and GitHub .

Shared database antipattern. A three-legged race

Published: Fri, 11 Feb 2022 00:00:00 +0000
Updated: Fri, 11 Feb 2022 00:00:00 +0000
UTC: 2022-02-11 00:00:00+00:00
URL: https://roman.pt/posts/three-legged-race/

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. Also, more often, I’ve seen its less obvious variant. There’s a legacy system that’s hard to maintain. When the business needs new functionality quickly, they recruit new developers to create a better service from scratch. To use the legacy system data, the development team explores existing data structures and connects their new code to the old database.
Content Preview
Three Legged Race

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.

Also, more often, I’ve seen its less obvious variant. There’s a legacy system that’s hard to maintain. When the business needs new functionality quickly, they recruit new developers to create a better service from scratch. To use the legacy system data, the development team explores existing data structures and connects their new code to the old database.

Tech debt hackathons is a waste of time

Published: Mon, 31 Jan 2022 00:00:00 +0000
Updated: Mon, 31 Jan 2022 00:00:00 +0000
UTC: 2022-01-31 00:00:00+00:00
URL: https://roman.pt/posts/tech-debt-hackathons-is-a-waste-of-time/

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 disregard its alerts.
Content Preview
Rushing towards the backlog
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 disregard its alerts.

You can use Pydantic in SQLAlchemy fields

Published: Wed, 19 Jan 2022 00:00:00 +0000
Updated: Wed, 19 Jan 2022 00:00:00 +0000
UTC: 2022-01-19 00:00:00+00:00
URL: https://roman.pt/posts/pydantic-in-sqlalchemy-fields/

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 of 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 storing unstructured data.
Content Preview

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 of 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 storing unstructured data.

Dealing with large pull requests

Published: Sat, 08 Jan 2022 00:00:00 +0000
Updated: Sat, 08 Jan 2022 00:00:00 +0000
UTC: 2022-01-08 00:00:00+00:00
URL: https://roman.pt/posts/large-pull-requests/

Photo by Elise Coates Sometimes, my pull requests grow too big to review. I want to split them into small chunks to be reviewed and merged independently. At the same time, I want to keep working on the feature in a branch, creating newer pull requests until the work is done. Automating PR stacks GitHub has a stacked pull requests thing. For two PRs, you can define their base to say “I want to merge PR 2 into the PR 1. I want to merge PR 1 into the main branch.” This is helpful when you work on a large code change, but want to review and merge it in independent smaller chunks. Managing these stacks of PRs is tedious, though.
Content Preview
Fragility of adding new code to stacked pull requests
Photo by Elise Coates

Sometimes, my pull requests grow too big to review. I want to split them into small chunks to be reviewed and merged independently. At the same time, I want to keep working on the feature in a branch, creating newer pull requests until the work is done.

Automating PR stacks

GitHub has a stacked pull requests thing. For two PRs, you can define their base to say “I want to merge PR 2 into the PR 1. I want to merge PR 1 into the main branch.” This is helpful when you work on a large code change, but want to review and merge it in independent smaller chunks. Managing these stacks of PRs is tedious, though.

Optimize pip install with wheels

Published: Wed, 22 Dec 2021 00:00:00 +0000
Updated: Wed, 22 Dec 2021 00:00:00 +0000
UTC: 2021-12-22 00:00:00+00:00
URL: https://roman.pt/posts/optimize-pip-install-with-wheels/

Photo by Jon Cartagena Having that pip install -r requirements.txt that takes ages to install? Make sure you install all binary packages from wheels. When “pip install” can’t find a wheel, it falls back to installing from source. In a large project, dependency after dependency, it slows down builds and makes developers unhappy. What are Python wheels and why they are so fast Long ago, most Python packages were distributed as source code with .tar.gz. The installed downloaded the archive and ran the setup.py script inside it. This format is known as “sdist”, source distribution.
Content Preview
wheels to optimize your installation
Photo by Jon Cartagena

Having that pip install -r requirements.txt that takes ages to install? Make sure you install all binary packages from wheels.

When “pip install” can’t find a wheel, it falls back to installing from source. In a large project, dependency after dependency, it slows down builds and makes developers unhappy.

What are Python wheels and why they are so fast

Long ago, most Python packages were distributed as source code with .tar.gz . The installed downloaded the archive and ran the setup.py script inside it. This format is known as “sdist”, source distribution.

How to pretty print SQL from Django

Published: Tue, 21 Dec 2021 00:00:00 +0000
Updated: Tue, 21 Dec 2021 00:00:00 +0000
UTC: 2021-12-21 00:00:00+00:00
URL: https://roman.pt/posts/pretty-print-django-sql/

Often, I find myself experimenting in the Django console with ad-hoc model queries. Where it’s not clear how Django turns a queryset to SQL, I find it helpful to print the resulting query. Django QuerySet object has a query attribute. However, its format would benefit from extra formatting. I used pygments and sqlparse to make the output of query more developer-friendly. pip install pygments sqlparse The snippet itself is very straightforward. # file sql_utils/utils.py from pygments import highlight from pygments.formatters import TerminalFormatter from pygments.lexers import PostgresLexer from sqlparse import format from django.db.models import QuerySet def print_sql(queryset: QuerySet): formatted = format(str(queryset.query), reindent=True) print(highlight(formatted, PostgresLexer(), TerminalFormatter())) This is how it looks like on the screenshot.
Content Preview

Often, I find myself experimenting in the Django console with ad-hoc model queries.

Where it’s not clear how Django turns a queryset to SQL, I find it helpful to print the resulting query. Django QuerySet object has a query attribute. However, its format would benefit from extra formatting.

I used pygments and sqlparse to make the output of query more developer-friendly.

pip install pygments sqlparse

The snippet itself is very straightforward.

# file sql_utils/utils.py

from pygments import highlight
from pygments.formatters import TerminalFormatter
from pygments.lexers import PostgresLexer
from sqlparse import format
from django.db.models import QuerySet


def print_sql(queryset: QuerySet):
 formatted = format(str(queryset.query), reindent=True)
 print(highlight(formatted, PostgresLexer(), TerminalFormatter()))

This is how it looks like on the screenshot.

User Stories for small teams and individuals

Published: Fri, 10 Dec 2021 00:00:00 +0000
Updated: Fri, 10 Dec 2021 00:00:00 +0000
UTC: 2021-12-10 00:00:00+00:00
URL: https://roman.pt/posts/user-stories/

Photo by AbsolutVision My wake up call I should admit that all my life, I believed that user stories are feature requests with a fancy and rigid ticket structure. My personal experience is limited. Last week, I had to explain the concept of user stories to a different person, only to learn that I knew nothing about the subject and mostly cargo-culted them. It’s time to fill the gap. Below I’m sharing my understanding of user stories and notes that I extracted from different sources in the context of a small team or a personal project.
Content Preview
User story ticket
Photo by AbsolutVision

My wake up call

I should admit that all my life, I believed that user stories are feature requests with a fancy and rigid ticket structure. My personal experience is limited.

Last week, I had to explain the concept of user stories to a different person, only to learn that I knew nothing about the subject and mostly cargo-culted them. It’s time to fill the gap. Below I’m sharing my understanding of user stories and notes that I extracted from different sources in the context of a small team or a personal project.

SQLAlchemy and Alembic notes

Published: Sun, 28 Nov 2021 00:00:00 +0000
Updated: Sun, 28 Nov 2021 00:00:00 +0000
UTC: 2021-11-28 00:00:00+00:00
URL: https://roman.pt/posts/sqlalchemy-and-alembic/

My notes on SQLAlchemy and Alembic
Content Preview

My notes on SQLAlchemy and Alembic

Quickstart snippet

A quickstart snippet for the SQLAlchemy ORM playground.

# file: playground.py
import sqlalchemy as sa
from sqlalchemy.orm import declarative_base, sessionmaker
import datetime

engine = sa.create_engine("sqlite:///:memory:", echo=True)
Session = sessionmaker(bind=engine, expire_on_commit=False)

Base = declarative_base()

class User(Base):
 __tablename__ = "users"
 id = sa.Column(sa.Integer, primary_key=True)
 name = sa.Column(sa.String, nullable=False)

Base.metadata.create_all(engine)
session = Session()

You can run the script with IPython interactive mode.

$ ipython -i playground.py

...
In [1]: session.add(User(name="foo"))
In [2]: session.commit()

SQLAlchemy pitfalls

Columns are nullable by default

The default value of SQLAlchemy nullable is False unless it’s a primary key. A foreign key is also nullable by default. If you came from the Django ORM, where the default values for fields are null=False, blank=False , you might accidentally create table definitions with nullable fields, which bites you in the future.

Codemaps Leaflet

Published: Tue, 09 Nov 2021 11:23:17 +0000
Updated: Tue, 09 Nov 2021 11:23:17 +0000
UTC: 2021-11-09 11:23:17+00:00
URL: https://roman.pt/posts/codemaps-leaflet/

Codemaps is my project to visualize git repos content using the treemaps visualization technique, available at https://usecodemaps.com/. Codemaps of CPython repo At the moment, it uses plotly treemaps and React Plotly.js to display the maps. I liked how it worked, but I didn’t particularly appreciate how sluggish it was on large repositories. Besides, plotly didn’t let me tweak the visualization part as much as I wanted. I thought it would be nice to reuse a JavaScript library that shows geographic maps to show the codemaps. The functionality overlap should be enough for me to climb onto their shoulders and take advantage of their out-of-the-box zoom and scale functionality, top-notch performance, etc.
Content Preview

Codemaps is my project to visualize git repos content using the treemaps visualization technique, available at https://usecodemaps.com/ .

Codemaps of CPython repo

Codemaps of CPython repo

At the moment, it uses plotly treemaps and React Plotly.js to display the maps. I liked how it worked, but I didn’t particularly appreciate how sluggish it was on large repositories. Besides, plotly didn’t let me tweak the visualization part as much as I wanted.

I thought it would be nice to reuse a JavaScript library that shows geographic maps to show the codemaps. The functionality overlap should be enough for me to climb onto their shoulders and take advantage of their out-of-the-box zoom and scale functionality, top-notch performance, etc.

Time Series Caching with Python and Redis

Published: Mon, 01 Nov 2021 11:25:00 +0000
Updated: Mon, 01 Nov 2021 11:25:00 +0000
UTC: 2021-11-01 11:25:00+00:00
URL: https://roman.pt/posts/time-series-caching/

Time series plot for historical pricing of Ethereum transfers Some takeaways on practical time series caching with Python and Redis from the Dashboards project that I implemented with Anyblock Analytics in 2021. The full code of the sample project is available at github.com/imankulov/time-series-caching. TLDR. We replaced generic Flask-caching solution with caching by date buckets, and I liked it. I like it so much that I decided to write a blog post, sharing how we came to the solution. More specifically:
Content Preview
Time series plot for historical pricing of Ethereum transfers

Time series plot for historical pricing of Ethereum transfers

Some takeaways on practical time series caching with Python and Redis from the Dashboards project that I implemented with Anyblock Analytics in 2021. The full code of the sample project is available at github.com/imankulov/time-series-caching .

TLDR. We replaced generic Flask-caching solution with caching by date buckets, and I liked it .

I like it so much that I decided to write a blog post, sharing how we came to the solution. More specifically:

Flow: The Psychology of Optimal Experience

Published: Fri, 22 Oct 2021 10:11:00 +0100
Updated: Fri, 22 Oct 2021 10:11:00 +0100
UTC: 2021-10-22 09:11:00+00:00
URL: https://roman.pt/posts/flow-book/

Key idea: our happiness can depend more on our state of mind than on external factors. Almost any activity can become the source of happiness if you nerd in deep enough. The key is to find the activity that you can easily turn into a game and start enjoying the process rather than waiting for the outcome. of learning and self-improvement. Google “8 elements of Flow.” The book is worth starting but not necessarily finishing. I abandoned it about halfway through when I became annoyed with the pattern “you can find enjoyment, doing X, here’s how” for different X.
Content Preview

Key idea: our happiness can depend more on our state of mind than on external factors.

Almost any activity can become the source of happiness if you nerd in deep enough. The key is to find the activity that you can easily turn into a game and start enjoying the process rather than waiting for the outcome. of learning and self-improvement. Google “8 elements of Flow.”

The book is worth starting but not necessarily finishing. I abandoned it about halfway through when I became annoyed with the pattern “you can find enjoyment, doing X, here’s how” for different X.

Structure Flask Project With Convention Over Configuration

Published: Wed, 05 May 2021 09:44:26 +0100
Updated: Wed, 05 May 2021 09:44:26 +0100
UTC: 2021-05-05 08:44:26+00:00
URL: https://roman.pt/posts/structure-flask-project/

Photo by Simone Viani When people ask me what a should beginner Python developer choose, Flask or Django, to start their new web project, all other things being equal, I always recommend Django. Unlike Flask, Django is opinionated. In other words, every time you need to make a choice in Flask, Django has the answer for you. Among others, Django outlines the practical project structure out of the box.
Content Preview
Flask Project With Convention Over Configuration
Photo by Simone Viani

When people ask me what a should beginner Python developer choose, Flask or Django, to start their new web project, all other things being equal, I always recommend Django.

Unlike Flask, Django is opinionated. In other words, every time you need to make a choice in Flask, Django has the answer for you. Among others, Django outlines the practical project structure out of the box.

21 tools to document your Python project

Published: Wed, 17 Mar 2021 11:00:00 +0000
Updated: Wed, 17 Mar 2021 11:00:00 +0000
UTC: 2021-03-17 11:00:00+00:00
URL: https://roman.pt/posts/21-tools-to-document-your-python-project/

Photo by Aaron Burden Overview of tools and services to document your Python web application from installation instructions to public API. How to make sure API documentation is in sync with your code. How to serve internal documentation and keep it private. Why documentation matters One of the best questions you can ask a team before joining it, is whether it has any documentation. As for restaurants, dirty bathrooms say dirty kitchen; for software companies, poor documentation smells rusty software design and poor processes.
Content Preview
Writing tooling is indispensable
Photo by Aaron Burden

Overview of tools and services to document your Python web application from installation instructions to public API. How to make sure API documentation is in sync with your code. How to serve internal documentation and keep it private.

Why documentation matters

One of the best questions you can ask a team before joining it, is whether it has any documentation. As for restaurants, dirty bathrooms say dirty kitchen; for software companies, poor documentation smells rusty software design and poor processes.

Linter for Python Architecture

Published: Tue, 23 Feb 2021 16:07:08 +0000
Updated: Tue, 23 Feb 2021 16:07:08 +0000
UTC: 2021-02-23 16:07:08+00:00
URL: https://roman.pt/posts/python-architecture-linter/

The goal of the architecture is to organize and constrain the dependencies between the components of your project. For example, in a regular Django project, views depend on models, and not vise versa. If you have utility functions, they shouldn’t depend on any specificities of your project. I was playing with import-linter, and it looks promising. What import-linter does In Python projects, we define dependencies by imports. If module A imports module B, then A depends on B. The import-linter lets you specify the rules that declaratively constrain that dependency flow. In a config file, you define so-called contracts. For example, one contract can say, “in my projects, models.py must not have any imports from the views.py.”
Content Preview

The goal of the architecture is to organize and constrain the dependencies between the components of your project. For example, in a regular Django project, views depend on models, and not vise versa. If you have utility functions, they shouldn’t depend on any specificities of your project.

I was playing with import-linter , and it looks promising.

What import-linter does

In Python projects, we define dependencies by imports. If module A imports module B, then A depends on B. The import-linter lets you specify the rules that declaratively constrain that dependency flow. In a config file, you define so-called contracts. For example, one contract can say, “in my projects, models.py must not have any imports from the views.py.”

Product Management. Explanation for Developers

Published: Mon, 21 Dec 2020 09:29:19 +0000
Updated: Mon, 21 Dec 2020 09:29:19 +0000
UTC: 2020-12-21 09:29:19+00:00
URL: https://roman.pt/posts/product-management/

If you work in a small company, chances are, your organization never had a dedicated role of a product manager. In startups, a product manager is one of the “hats” of the company’s founder. Founders use their vision and gut feelings to identify where the project should move. From a developer’s perspective, the role and the value of a product manager are not clear.
Content Preview If you work in a small company, chances are, your organization never had a dedicated role of a product manager. In startups, a product manager is one of the “hats” of the company’s founder. Founders use their vision and gut feelings to identify where the project should move. From a developer’s perspective, the role and the value of a product manager are not clear.

Meeting culture for seniors and veterans

Published: Sun, 06 Dec 2020 12:56:14 +0000
Updated: Sun, 06 Dec 2020 12:56:14 +0000
UTC: 2020-12-06 12:56:14+00:00
URL: https://roman.pt/posts/meeting-culture-for-seniors-and-veterans/

Photo by Mihai Surdu I often see seniors and veterans in the organization making two mistakes: talk when it’s time to listen and not knowing when to shut up. I am guilty as well, so here’s the memo for myself. There’s time to talk, and there’s time to listen. If you’re familiar with the topic, you may feel a burning desire to jump in with your thoughts. Besides, if you are a veteran, the threshold for starting talking for you is low. Catch yourself, take a pause. Wait longer, embrace the awkwardness of the moment. Invite a newcomer to the discussion. The barrier to begin speaking up is higher for a newcomer, but they provide a different perspective to a problem.
Content Preview
Meeting
Photo by Mihai Surdu

I often see seniors and veterans in the organization making two mistakes: talk when it’s time to listen and not knowing when to shut up. I am guilty as well, so here’s the memo for myself.

There’s time to talk, and there’s time to listen. If you’re familiar with the topic, you may feel a burning desire to jump in with your thoughts. Besides, if you are a veteran, the threshold for starting talking for you is low. Catch yourself, take a pause. Wait longer, embrace the awkwardness of the moment. Invite a newcomer to the discussion. The barrier to begin speaking up is higher for a newcomer, but they provide a different perspective to a problem.

Don't let dicts spoil your code

Published: Sun, 22 Nov 2020 00:00:00 +0000
Updated: Sun, 22 Nov 2020 00:00:00 +0000
UTC: 2020-11-22 00:00:00+00:00
URL: https://roman.pt/posts/dont-let-dicts-spoil-your-code/

Photo by Martin Sanchez How often do your simple prototypes or ad-hoc scripts turn into fully-fledged applications? The simplicity of organic code growth has a flip side: it becomes too hard to maintain. The proliferation of dicts as primary data structures is a clear signal of tech debt in your code. Fortunately, modern Python provides many viable alternatives to plain dicts. What’s wrong with dicts? Dicts are opaque Functions that accept dicts are a nightmare to extend and modify. Usually, to change the function that takes a dictionary, you must manually trace the calls back to the roots, where this dict was created. There is often more than one call path, and if a program grows without a plan, you’ll likely have discrepancies in the dict structures.
Content Preview
Balance
Photo by Martin Sanchez

How often do your simple prototypes or ad-hoc scripts turn into fully-fledged applications?

The simplicity of organic code growth has a flip side: it becomes too hard to maintain. The proliferation of dicts as primary data structures is a clear signal of tech debt in your code. Fortunately, modern Python provides many viable alternatives to plain dicts.

What’s wrong with dicts?

Dicts are opaque

Functions that accept dicts are a nightmare to extend and modify. Usually, to change the function that takes a dictionary, you must manually trace the calls back to the roots, where this dict was created. There is often more than one call path, and if a program grows without a plan, you’ll likely have discrepancies in the dict structures.

4 Reading mistakes I made

Published: Fri, 20 Nov 2020 07:41:05 +0000
Updated: Fri, 20 Nov 2020 07:41:05 +0000
UTC: 2020-11-20 07:41:05+00:00
URL: https://roman.pt/posts/reading-mistakes/

Photo by Giancarlo Duarte My thoughts on how to become a better reader. The context is non-fiction books. Not reading enough. I decided that there shouldn’t be a moment when I don’t have any book reading. Also, I try not to have a day where I don’t read at least a bit. I know that people don’t like electronic books, but this is my primary way of reading. I always have a tab with a currently reading book pinned in my browser. If I am tired and want to distract myself, I switch to that tab instead of opening a news site or a social feed.
Content Preview
Reading
Photo by Giancarlo Duarte

My thoughts on how to become a better reader. The context is non-fiction books.

Not reading enough. I decided that there shouldn’t be a moment when I don’t have any book reading. Also, I try not to have a day where I don’t read at least a bit. I know that people don’t like electronic books, but this is my primary way of reading. I always have a tab with a currently reading book pinned in my browser. If I am tired and want to distract myself, I switch to that tab instead of opening a news site or a social feed.

Alignment Matters

Published: Sun, 25 Oct 2020 07:42:25 +0000
Updated: Sun, 25 Oct 2020 07:42:25 +0000
UTC: 2020-10-25 07:42:25+00:00
URL: https://roman.pt/posts/alignment-matters/

Thought on hiring and career planning in application to software development. Skills for sale Imagine you can somehow measure your skills as you measure your weight. In reality, of course, a single number doesn’t catch the multitude of nuances of your traits. An area can become a better representation of your skills. We usually call it “area of my expertise.” I call it amoeba to emphasize that it’s a living creature. It’s moving and growing.
Content Preview

Thought on hiring and career planning in application to software development.

Skills for sale

Imagine you can somehow measure your skills as you measure your weight.

A box with the text my skills and 86

In reality, of course, a single number doesn’t catch the multitude of nuances of your traits. An area can become a better representation of your skills. We usually call it “area of my expertise.” I call it amoeba to emphasize that it’s a living creature. It’s moving and growing.

Python code cleanup for beginners. 12 steps to readable and maintainable code.

Published: Fri, 18 Sep 2020 10:52:00 +0100
Updated: Fri, 18 Sep 2020 10:52:00 +0100
UTC: 2020-09-18 09:52:00+00:00
URL: https://roman.pt/posts/python-cleanup/

Photo by Lea Verou It’s my responsibility to hire Python developers. If you have a GitHub account, I will check it out. Everyone does it. Maybe you don’t know, but your playground project with zero stars can help you to get that job offer. The same goes for your take-home test task. You heard this, when we meet a person for the first time, we make the first impression in 30 seconds, and this biases the rest of our judgment. I find it uncomfortably unfair that beautiful people get everything easier than the rest of us. The same bias applies to the code. You look at the project, and something immediately catches your eye. Leftovers of the old code in the repo, like leftovers of your breakfast in your beard, can ruin everything. You may not have a beard, but you get the point.
Content Preview
Programmer Priorities
Photo by Lea Verou

It’s my responsibility to hire Python developers. If you have a GitHub account, I will check it out. Everyone does it. Maybe you don’t know, but your playground project with zero stars can help you to get that job offer.

The same goes for your take-home test task. You heard this, when we meet a person for the first time, we make the first impression in 30 seconds, and this biases the rest of our judgment. I find it uncomfortably unfair that beautiful people get everything easier than the rest of us. The same bias applies to the code. You look at the project, and something immediately catches your eye. Leftovers of the old code in the repo, like leftovers of your breakfast in your beard, can ruin everything. You may not have a beard, but you get the point.

Boring Meetings and Multitasking

Published: Thu, 10 Sep 2020 08:48:16 +0100
Updated: Thu, 10 Sep 2020 08:48:16 +0100
UTC: 2020-09-10 07:48:16+00:00
URL: https://roman.pt/posts/on-boring-meetings-and-multitasking/

Photo by Chris Montgomery I use multitasking as a measure of the success of our regular team meetings. If someone has time to reply to emails or finish their code during the meeting, then the topic doesn’t concern them, and it’s a waste of their time. It’s that easy. The worst thing you can do here is to reprehend. Saying that someone is not paying attention and you are disappointed is a reaction of a lousy school teacher. Effectively, you just got a very clear signal, but you prefer to ignore it and shoot the messenger. Instead, when I talk with my colleagues, I emphasize that multitasking is valuable information for the facilitator and a sign that it’s time to change something. I ask people to tell me if they catch themselves multitasking. Sometimes I even ask people directly how often do they feel distracted at the team meetings.
Content Preview
Meeting
Photo by Chris Montgomery

I use multitasking as a measure of the success of our regular team meetings.

If someone has time to reply to emails or finish their code during the meeting, then the topic doesn’t concern them, and it’s a waste of their time. It’s that easy.

The worst thing you can do here is to reprehend. Saying that someone is not paying attention and you are disappointed is a reaction of a lousy school teacher. Effectively, you just got a very clear signal, but you prefer to ignore it and shoot the messenger. Instead, when I talk with my colleagues, I emphasize that multitasking is valuable information for the facilitator and a sign that it’s time to change something. I ask people to tell me if they catch themselves multitasking. Sometimes I even ask people directly how often do they feel distracted at the team meetings.

On Burnout

Published: Wed, 02 Sep 2020 09:26:04 +0100
Updated: Wed, 02 Sep 2020 09:26:04 +0100
UTC: 2020-09-02 08:26:04+00:00
URL: https://roman.pt/posts/on-burnout/

My summary of the video The Thing About Burnout from @ashleymcnamara.
Content Preview

My summary of the video The Thing About Burnout from @ashleymcnamara .

Symptoms and development

  • Burnout is like PTSD. After some recovery time, you may feel you’re OK, but some triggers can get you back to the burnout state.
  • The burnout recovery time is two years. A two-week-long vacation doesn’t help.
  • The process is burning out is slow. It can take years.

Phases of burnout

  • honeymoon
  • onset of stress: you start feeling less motivated
  • neglecting self: overeating, etc.
  • losing motivation: can’t get excited about the work anymore
  • physical issues: digestion issues, tinnitus, etc.

How to avoid burnout?

  • Get clarity. What is the purpose of your work? What are you supposed to do? What are the expectations?
  • Stop multi-tasking, and say no more often.
  • Take control of your time: plan your work, turn off your notifications.
  • Get enough sleep.

Tips for managers

  • Be clear about expectations.
  • Help people understand their values
  • Enforce reasonable work hours.
  • Don’t contact your people after 5 PM.
  • Encourage people to take their time off.
  • Have regular 1-on-1’s and actively listen if people feel stressed.
  • Don’t recognize only big wins. Small wins count!

Final take

Remember about your family. They suffer from your burnout no less than yourself.

5 reasons to love Python type hints

Published: Tue, 25 Aug 2020 06:53:40 +0100
Updated: Tue, 25 Aug 2020 06:53:40 +0100
UTC: 2020-08-25 05:53:40+00:00
URL: https://roman.pt/posts/python-type-hints/

I’m a big fan of type hints, and I honestly don’t understand why some people find it ugly or unpythonic. They improve the auto-complete and code navigation in the IDE. They protect you against trivial errors and remind you about corner cases. Beyond IDEs, more and more tools in the Python ecosystem take advantage of typing information. The FastAPI framework is the first example that comes to my mind. They are like documentation, that is testable, both human- and machine-readable, and never outdated. They make you think about your code and highlight code smell. Do you need to return Optional, or it’s better to implement a null object pattern? Do you need to return a Union or split your function in two? Overall, they make explicit many implicit assumptions and connections in your code. Maybe people who complain that type hints are ugly fail to admit that type hints just uncover deficiencies of their own system?
Content Preview

I’m a big fan of type hints, and I honestly don’t understand why some people find it ugly or unpythonic.

  1. They improve the auto-complete and code navigation in the IDE.
  2. They protect you against trivial errors and remind you about corner cases.
  3. Beyond IDEs, more and more tools in the Python ecosystem take advantage of typing information. The FastAPI framework is the first example that comes to my mind.
  4. They are like documentation, that is testable, both human- and machine-readable, and never outdated.
  5. They make you think about your code and highlight code smell. Do you need to return Optional, or it’s better to implement a null object pattern? Do you need to return a Union or split your function in two?

Overall, they make explicit many implicit assumptions and connections in your code. Maybe people who complain that type hints are ugly fail to admit that type hints just uncover deficiencies of their own system?

Accelerate. Five-minute summary

Published: Sat, 15 Aug 2020 13:28:00 +0100
Updated: Sat, 15 Aug 2020 13:28:00 +0100
UTC: 2020-08-15 12:28:00+00:00
URL: https://roman.pt/posts/accelerate/

A five-minute summary of the book “Accelerate: The Science of Lean Software and DevOps” by Nicole Forsgren, Jez Humble, and Gene Kim. Photo by Ahmad Odeh About the book The overall idea of “Accelerate” is to find how engineering practices affect companies’ performance and the well-being of their employees. This book feels like two books under the same cover. The first part, “What we found,” is practical, while the second, “The research,” explains in detail the science behind the book. Finally, there’s a third smaller part, “Transformation,” with a case study. I read the first part and only skimmed thought the second and the third.
Content Preview

A five-minute summary of the book “Accelerate: The Science of Lean Software and DevOps” by Nicole Forsgren, Jez Humble, and Gene Kim.

Accelerating
Photo by Ahmad Odeh

About the book

The overall idea of “Accelerate” is to find how engineering practices affect companies’ performance and the well-being of their employees.

This book feels like two books under the same cover. The first part, “What we found,” is practical, while the second, “The research,” explains in detail the science behind the book. Finally, there’s a third smaller part, “Transformation,” with a case study. I read the first part and only skimmed thought the second and the third.

Feature Branching vs. Continuous Integration

Published: Tue, 12 May 2020 10:06:03 +0100
Updated: Tue, 12 May 2020 10:06:03 +0100
UTC: 2020-05-12 09:06:03+00:00
URL: https://roman.pt/posts/on-feature-branching-vs-continuous-integration/

My summary and takeaways from Martin Fowler’s Patterns for Managing Source Code Branches.
Content Preview
Balance
Photo by Sophie Dale

My summary and takeaways from Martin Fowler’s Patterns for Managing Source Code Branches .

Summary

  • Having a CI pipeline, running tests on every commit is not Continuous Integration.
  • Copying Open Source best practices to commercial development without understanding their context and limitations can be harmful.
  • Feature branches hinder refactoring.
  • Slow code reviews kill Continuous Integration, and it’s a common problem.
  • If you want CI, but can’t do fast code reviews, consider alternatives. Code reviews are the most common but not the only way to warrant the quality of the code.

Semantic conflicts

When you merge your changes back to the master branch, two types of conflicts may occur. Textual conflicts are those that prevent git from merging back your work without resolving them. Semantic conflicts are more subtle and harder to spot. An example of a semantic conflict is when one developer renames or moves a function, and another developer adds new calls for the old version of the function. It’s especially dangerous for Python, and without proper automated tests, unresolved semantic conflict become runtime errors. To minimize the effect of the conflicts, Martin recommends integrating back your changes as often as possible. The rule of thumb is never to have branches that contain more than a day worth of changes.

On the Code Ownership

Published: Sun, 03 May 2020 09:58:50 +0100
Updated: Sun, 03 May 2020 09:58:50 +0100
UTC: 2020-05-03 08:58:50+00:00
URL: https://roman.pt/posts/on-the-code-ownership/

Photo by Goran Ivos Code ownership is a smell, and we need to avoid it. When several developers contribute to the same codebase, several great minds are thinking of the same problem, which eventually results in a better solution. On the opposite side of the spectrum, with strong code ownership, you inevitably end up with a code that is easier to rewrite than to understand by others. I took this as granted until I came across a Microsoft research that contradicted my common sense. They showed that code ownership results in higher code quality and fewer bugs.
Content Preview
Code
Photo by Goran Ivos

Code ownership is a smell, and we need to avoid it. When several developers contribute to the same codebase, several great minds are thinking of the same problem, which eventually results in a better solution. On the opposite side of the spectrum, with strong code ownership, you inevitably end up with a code that is easier to rewrite than to understand by others.

I took this as granted until I came across a Microsoft research that contradicted my common sense. They showed that code ownership results in higher code quality and fewer bugs.

On Feature Flags at Doist

Published: Tue, 21 Apr 2020 10:20:38 +0100
Updated: Tue, 21 Apr 2020 10:20:38 +0100
UTC: 2020-04-21 09:20:38+00:00
URL: https://roman.pt/posts/on-feature-flags-at-doist/

Photo by Dean Brierley I shared our experience introducing feature flags in the tech ecosystem at Doist with our partners from CloudBees. https://www.cloudbees.com/case-study/doist CloudBees, known for rollout.io asked us to share our case study, and it was good opportunity to reflect on our journey towards the feature-flag based development, what we achieved so far and where we’re heading towards. Also, it served me as a reminder how many different use cases unlocks such a simple idea.
Content Preview
Feature Toggles
Photo by Dean Brierley

I shared our experience introducing feature flags in the tech ecosystem at Doist with our partners from CloudBees.

https://www.cloudbees.com/case-study/doist

CloudBees, known for rollout.io asked us to share our case study, and it was good opportunity to reflect on our journey towards the feature-flag based development, what we achieved so far and where we’re heading towards. Also, it served me as a reminder how many different use cases unlocks such a simple idea.

About me

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/about/

I am a passionate full-stack web developer with almost 20 years of hands-on and management experience. I love helping startups owners and small teams turn their ideas into products. I am currently not actively looking for new projects. However, you can still reach me at roman.imankulov@gmail.com Zero to one. While I’m comfortable handling any technical challenge, I truly excel in building things from scratch. I have a particular passion for the creative problem-solving required in early-stage development, where we’re turning abstract ideas into concrete solutions. This is where my broad technical expertise combines with creative thinking to find elegant, practical approaches to complex problems. I enjoy the challenge of making foundational architectural decisions that will shape the project’s future.
Content Preview

I am a passionate full-stack web developer with almost 20 years of hands-on and management experience. I love helping startups owners and small teams turn their ideas into products.

I am currently not actively looking for new projects. However, you can still reach me at roman.imankulov@gmail.com

Zero to one. While I’m comfortable handling any technical challenge, I truly excel in building things from scratch. I have a particular passion for the creative problem-solving required in early-stage development, where we’re turning abstract ideas into concrete solutions. This is where my broad technical expertise combines with creative thinking to find elegant, practical approaches to complex problems. I enjoy the challenge of making foundational architectural decisions that will shape the project’s future.

AWS

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/snippets/aws/

Go-to region Use us-east-2 aka US East (Ohio). Set up personal AWS profile to use echo "export AWS_PROFILE=personal" >> .startup Account info for your current requisites aws sts get-caller-identity
Content Preview

Go-to region

Use us-east-2 aka US East (Ohio).

Set up personal AWS profile to use

echo "export AWS_PROFILE=personal" >> .startup

Account info for your current requisites

aws sts get-caller-identity

Docker and docker-compose

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/snippets/docker/

Helpful services composerize: convert docker run to docker-compose Docker commands Reclaim disk space Clean up disk space by removing all the docker containers older than 14 days. docker image prune -a --filter "until=336h" See Prune unused Docker objects Run a container and attach current directory Example for a specific version of Python. docker run -v $(pwd):/app --rm -it python:3.10 bash Docker-compose Snippets PostgreSQL Sample docker-compose file to run a PostgreSQL server. # The storage can be cleaned up by running the command: # docker-compose down -v # # Django-environ settings: # DATABASE_URL=psql://myproject:password@127.0.0.1:5499/myproject version: '3.3' services: db: image: postgres:14 environment: POSTGRES_USER: myproject POSTGRES_PASSWORD: password POSTGRES_DB: myproject ports: - '127.0.0.1:5499:5432' volumes: - 'postgres-data:/var/lib/postgresql/data' volumes: postgres-data: {} MongoDB Sample docker-compose file to run a MongoDB server.
Content Preview

Helpful services

Docker commands

Reclaim disk space

Clean up disk space by removing all the docker containers older than 14 days.

docker image prune -a --filter "until=336h"

See Prune unused Docker objects

Run a container and attach current directory

Example for a specific version of Python.

docker run -v $(pwd):/app --rm -it python:3.10 bash

Docker-compose Snippets

PostgreSQL

Sample docker-compose file to run a PostgreSQL server.

# The storage can be cleaned up by running the command:
# docker-compose down -v
#
# Django-environ settings:
# DATABASE_URL=psql://myproject:password@127.0.0.1:5499/myproject
version: '3.3'

services:
 db:
 image: postgres:14
 environment:
 POSTGRES_USER: myproject
 POSTGRES_PASSWORD: password
 POSTGRES_DB: myproject
 ports:
 - '127.0.0.1:5499:5432'
 volumes:
 - 'postgres-data:/var/lib/postgresql/data'

volumes:
 postgres-data: {}

MongoDB

Sample docker-compose file to run a MongoDB server.

External links

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/links/

Timeless blog posts and articles. I found myself sharing these links over and over again, so I decided to put them here. Software development Questions on software development and organization of team work. Ship / Show / Ask. A modern branching strategy on how to relax code review process without sacrificing quality. Frequency reduces difficulty on why you need to deploy more often if the deployment is painful. The Null Process on how the fear of introducing unnecessary processes can hurt your team. Practical software architecture Architecture solutions for real-world problems.
Content Preview

Timeless blog posts and articles. I found myself sharing these links over and over again, so I decided to put them here.

Software development

Questions on software development and organization of team work.

Practical software architecture

Architecture solutions for real-world problems.

Git

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/snippets/git/

Delete merged branches Leftovers from merged branches are distracting. Too many of them make commands like git branch -l useless. Here are some commands to clean them up. Get the list of all branches that have been merged into the current branch. git branch --merged Delete all branches that have been merged into the current branch. We exclude asterisk because that’s how we identify the current branch. git_main_branch is an Oh My Zsh function that returns the name of the main branch. git branch --merged "$(git_main_branch)" | grep -v "$(git_main_branch)" | grep -v '*' | xargs -n 1 git branch -d See if there is anything left among unmerged branches.
Content Preview

Delete merged branches

Leftovers from merged branches are distracting. Too many of them make commands like git branch -l useless . Here are some commands to clean them up.

Get the list of all branches that have been merged into the current branch.

git branch --merged

Delete all branches that have been merged into the current branch.

  • We exclude asterisk because that’s how we identify the current branch.
  • git_main_branch is an Oh My Zsh function that returns the name of the main branch.
git branch --merged "$(git_main_branch)" | grep -v "$(git_main_branch)" | grep -v '*' | xargs -n 1 git branch -d

See if there is anything left among unmerged branches.

Poetry

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/snippets/poetry/

Initialization Quickly initialize a new Python environment with Poetry. We explicitly stick to Python 3.11.x (we don’t want automatic upgrades to 3.12, etc.). Also, we activate Poetry virtual environment on startup. poetry init --python '~3.11' --no-interaction --quiet poetry install echo 'source $(poetry env info -p)/bin/activate' > .startup Resolving rebase conflicts When rebasing a branch with an updated poetry.lock, conflicts may arise. To resolve these conflicts, we can utilize the following function: function poetry-resolve-rebase-conflict() { git checkout --ours ./poetry.lock poetry lock --no-update git add ./poetry.lock }
Content Preview

Initialization

Quickly initialize a new Python environment with Poetry. We explicitly stick to Python 3.11.x (we don’t want automatic upgrades to 3.12, etc.). Also, we activate Poetry virtual environment on startup.

poetry init --python '~3.11' --no-interaction --quiet
poetry install
echo 'source $(poetry env info -p)/bin/activate' > .startup

Resolving rebase conflicts

When rebasing a branch with an updated poetry.lock, conflicts may arise. To resolve these conflicts, we can utilize the following function:

function poetry-resolve-rebase-conflict() {
 git checkout --ours ./poetry.lock
 poetry lock --no-update
 git add ./poetry.lock
}

Pydantic

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/snippets/pydantic/

See Pydantic model options for more options.
Content Preview

Project-specific models

See Pydantic model options for more options.

from pydantic import BaseModel


class Model(BaseModel):
 """Generic project-specific model."""

 class Config:
 # Allow field population by its name or alias
 # (helpful for mapping external API values)
 allow_population_by_field_name = True


class FrozenModel(BaseModel):
 """Generic project-specific frozen model."""

 class Config:
 # Make it frozen to allow using as dict keys
 frozen = True
 # Allow field population by its name or alias
 # (helpful for mapping external API values)
 allow_population_by_field_name = True

Integration with Arrow

import arrow

class PyArrow(arrow.Arrow):
 """FastAPI-serializable arrow instance."""

 @classmethod
 def __get_validators__(cls):
 yield cls.validate

 @classmethod
 def validate(cls, v):
 if v is None:
 raise ValueError("Invalid value")
 return arrow.get(v)

 @classmethod
 def __modify_schema__(cls, field_schema):
 field_schema.update(type="string")

FastAPI models

We want to return models with camelCase attributes, and we can automate it with Pydantic.

Python project

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/snippets/python-project/

Here is a starter for a Python project. It can be used as the next step after creating the boilerplate with cookiecutter-python-project
Content Preview

Here is a starter for a Python project. It can be used as the next step after creating the boilerplate with cookiecutter-python-project

Dependencies:

  • deescovery for dynamic discovery of modules
  • python-dotenv for loading environment variables from .env and .env.<environment> files (e.g., .env.development )
  • click for creating CLI commands
  • Optionally, IPython for interactive shell

File myproject/config.py

"""Configuration.

The module doesn't depend on the project. The configuration file is initialized
with python-dotenv, and we don't read environment variables in the rest of the
project.


What config.py can import?
--------------------------

The module doesn't import anything from the project.


Who can import config.py?
---------------------------

The module is imported by the app.py to create services. It can also be imported in
the other parts of the application: for example, to get access to PROJECT_ROOT, or to
create project-specific services from requisites, provided in the config.
"""
import os
import pathlib

import dotenv

PROJECT_ROOT = pathlib.Path(__file__).parents[1]
ENVIRONMENT = os.getenv("ENVIRONMENT", "development")

config = {
 **dotenv.dotenv_values((PROJECT_ROOT / ".env").as_posix()),
 **dotenv.dotenv_values((PROJECT_ROOT / f".env.{ENVIRONMENT}").as_posix()),
 **os.environ,
}

# --------------------------------------------------------------------------------
# Server configuration
# --------------------------------------------------------------------------------
HOST = config.get("HOST", "0.0.0.0")
PORT = int(config.get("PORT") or 5000)
STATIC_ROOT = "/static"
SECRET_KEY = config["SECRET_KEY"]

# --------------------------------------------------------------------------------
# Sentry settings
# --------------------------------------------------------------------------------
# Set SENTRY_DSN to a non-empty value to enable Sentry.
SENTRY_DSN = config.get("SENTRY_DSN")
SENTRY_ENVIRONMENT = config.get("SENTRY_ENVIRONMENT") or "development"

File myproject/services.py

Quote of the Day Privacy Policy

Published: Mon, 01 Jan 0001 00:00:00 +0000
Updated: Mon, 01 Jan 0001 00:00:00 +0000
UTC: 0001-01-01 00:00:00+00:00
URL: https://roman.pt/apps/qod/privacy/

Privacy Policy Roman Imankulov built the Quote of the Day app as a Free app. This SERVICE is provided by Roman Imankulov at no cost and is intended for use as is. This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy.
Content Preview

Privacy Policy

Roman Imankulov built the Quote of the Day app as a Free app. This SERVICE is provided by Roman Imankulov at no cost and is intended for use as is.

This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service.

If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy.