<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paweł Mazur</title>
    <description>The latest articles on DEV Community by Paweł Mazur (@efowski).</description>
    <link>https://dev.to/efowski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4102914%2F34bf9699-b201-47a5-8542-a0ff8df48621.jpg</url>
      <title>DEV Community: Paweł Mazur</title>
      <link>https://dev.to/efowski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/efowski"/>
    <language>en</language>
    <item>
      <title>RouteFlow — What Happened When I Finally Tested the Whole MVP</title>
      <dc:creator>Paweł Mazur</dc:creator>
      <pubDate>Wed, 09 Sep 2026 21:34:03 +0000</pubDate>
      <link>https://dev.to/efowski/routeflow-what-happened-when-i-finally-tested-the-whole-mvp-1bfj</link>
      <guid>https://dev.to/efowski/routeflow-what-happened-when-i-finally-tested-the-whole-mvp-1bfj</guid>
      <description>&lt;p&gt;In my previous RouteFlow post, I ended with a fairly simple goal: stop adding features and run the application from an empty account as if I were an actual climbing gym.&lt;/p&gt;

&lt;p&gt;So that's what I did.&lt;/p&gt;

&lt;p&gt;It didn't take very long to find the first problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing from an empty account
&lt;/h2&gt;

&lt;p&gt;Until now, most of RouteFlow had been tested while individual features were being built.&lt;/p&gt;

&lt;p&gt;This time I wanted to start from scratch and follow the actual process:&lt;/p&gt;

&lt;p&gt;Register Gym Manager → Create Gym → Add Setters → Plan Setting Session → Assign Tasks → Complete Work → Publish Route&lt;/p&gt;

&lt;p&gt;And I added one rule.&lt;/p&gt;

&lt;p&gt;If I had to open Django Admin or manually fix something in the database to make the normal workflow possible, then v0.1 wasn't ready yet.&lt;/p&gt;

&lt;p&gt;Simple enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first test passed
&lt;/h2&gt;

&lt;p&gt;I registered a completely new Gym Manager account.&lt;/p&gt;

&lt;p&gt;The application logged me in correctly. The gym name appeared in the interface. The user had the correct role. There was no data leaking in from another gym.&lt;/p&gt;

&lt;p&gt;Everything looked fine.&lt;/p&gt;

&lt;p&gt;So initially, I marked the first step as passed.It wasn't.&lt;/p&gt;

&lt;p&gt;When I checked the backend, I realised that registration had created the user, but it hadn't actually created the corresponding Gym.&lt;/p&gt;

&lt;p&gt;The UI knew the name of the gym.&lt;/p&gt;

&lt;p&gt;The database didn't know the gym existed.&lt;/p&gt;

&lt;p&gt;gym_name is not gym&lt;/p&gt;

&lt;p&gt;This came from a distinction I had deliberately made earlier in the project.&lt;/p&gt;

&lt;p&gt;The user model contains both a textual gym name and an actual relationship to the Gym model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gym = models.ForeignKey(
    Gym,
    null=True,
    blank=True,
    related_name='users',
    on_delete=models.SET_NULL
)

gym_name = models.CharField(
    max_length=150,
    default="VertiGym Warszawska"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction itself wasn't the problem.&lt;/p&gt;

&lt;p&gt;The registration serializer was.&lt;/p&gt;

&lt;p&gt;It could store gym_name, and it could accept an existing gym:&lt;/p&gt;

&lt;p&gt;gym=validated_data.get('gym'),&lt;br&gt;
gym_name=validated_data.get('gym_name', 'VertiGym'),&lt;/p&gt;

&lt;p&gt;But it didn't create a Gym when a new manager registered one.&lt;/p&gt;

&lt;p&gt;So I effectively had this:&lt;/p&gt;

&lt;p&gt;User&lt;br&gt;
├── gym_name = "RouteFlow Test Gym"&lt;br&gt;
└── gym = NULL&lt;/p&gt;

&lt;p&gt;when what I actually needed was:&lt;/p&gt;

&lt;p&gt;Gym&lt;br&gt;
└── name = "RouteFlow Test Gym"&lt;br&gt;
     ↑&lt;br&gt;
     │&lt;br&gt;
User&lt;br&gt;
└── gym = &lt;/p&gt;

&lt;p&gt;This was surprisingly easy to miss because nothing immediately broke.&lt;/p&gt;

&lt;p&gt;Registration worked. Login worked. The frontend displayed the gym name. From the user's perspective, the account looked valid.&lt;/p&gt;

&lt;p&gt;Only when I tested the application as a complete system did the difference between displaying a string and having an actual domain object become important.&lt;/p&gt;
&lt;h2&gt;
  
  
  The fix was small
&lt;/h2&gt;

&lt;p&gt;The solution wasn't particularly complicated.&lt;/p&gt;

&lt;p&gt;During registration, if no existing gym is supplied, the serializer now creates one using the submitted gym name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gym_name = validated_data.get(
    'gym_name',
    'VertiGym Warszawska'
)

gym = validated_data.get('gym')

if not gym:
    gym = Gym.objects.create(name=gym_name)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The newly created Gym is then assigned to the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user = User.objects.create_user(
    email=validated_data['email'],
    username=validated_data.get('username')
        or validated_data['email'],
    first_name=validated_data.get('first_name', ''),
    last_name=validated_data.get('last_name', ''),
    role=validated_data.get('role', 'gym_manager'),
    gym=gym,
    gym_name=gym_name,
    password=validated_data['password'],
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I repeated the test.&lt;/p&gt;

&lt;p&gt;New user: created.&lt;/p&gt;

&lt;p&gt;New gym: created.&lt;/p&gt;

&lt;p&gt;User assigned to that gym: yes.&lt;/p&gt;

&lt;p&gt;Problem solved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdjx12nub56lyvq65o0qy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdjx12nub56lyvq65o0qy.png" alt="RouteFlow Setting Planner showing a planned climbing gym setting session" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Then the actual workflow worked
&lt;/h2&gt;

&lt;p&gt;After fixing registration, I continued through the complete workflow: creating setters, planning a Setting Session, assigning tasks, logging in as a Route Setter and eventually publishing completed work as a Route.&lt;/p&gt;

&lt;p&gt;That part worked.&lt;/p&gt;

&lt;p&gt;So did the less visible parts I was particularly interested in testing: role-based permissions, persistence after refresh and data isolation between gyms.&lt;/p&gt;

&lt;p&gt;A Route Setter could work with their assigned tasks without getting access to manager-level operations. A Head Setter could manage the setting workflow without getting access to gym-level user administration.&lt;/p&gt;

&lt;p&gt;And a user from Gym A couldn't suddenly access Gym B's routes, setters, sessions or tasks.&lt;/p&gt;

&lt;p&gt;Most importantly, the core relationship I've been building RouteFlow around finally worked as one continuous process:&lt;/p&gt;

&lt;p&gt;Sector → Setting Session → Setter Task → Route&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6idtkiuqr23y2ipcqdjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6idtkiuqr23y2ipcqdjv.png" alt="RouteFlow Setter Tasks view showing route setting tasks and their workflow statuses" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I hit the second problem.I still had to open Django Admin.Sectors.&lt;/p&gt;

&lt;p&gt;The backend already understands them. Routes and Setting Sessions use them. Gym isolation applies to them.&lt;/p&gt;

&lt;p&gt;But there currently isn't a way for a new Gym Manager to create the initial sectors from the frontend.&lt;/p&gt;

&lt;p&gt;To continue the test, I had to create them manually through Django Admin.&lt;/p&gt;

&lt;p&gt;And that breaks the rule I set for myself before starting this test.&lt;/p&gt;

&lt;p&gt;In the previous post I wrote:&lt;/p&gt;

&lt;p&gt;If it requires developer intervention along the way, v0.1 isn't ready yet.&lt;/p&gt;

&lt;p&gt;Well.It did.So v0.1 isn't ready yet.Not quite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working features aren't necessarily a working product
&lt;/h2&gt;

&lt;p&gt;This was probably the most useful result of the whole test.Most of RouteFlow worked.The task workflow worked. Route publishing worked. Authentication worked. RBAC worked. Gym isolation worked. Setting Sessions worked.&lt;/p&gt;

&lt;p&gt;But a completely new customer still couldn't get from registration to actually using those features without me stepping in.&lt;/p&gt;

&lt;p&gt;That's a very different kind of failure from an API returning 500 or a React component crashing.&lt;/p&gt;

&lt;p&gt;And it's also much easier to miss when development consists of implementing one feature at a time.&lt;/p&gt;

&lt;p&gt;While building a feature, the database already contains the objects you need. You already have test users. Sectors exist because you created them weeks ago. Your account is already attached to a gym. The application slowly accumulates the environment it needs to work. A new user gets none of that.&lt;/p&gt;

&lt;p&gt;Starting again with an empty account removes all of those assumptions.&lt;/p&gt;

&lt;p&gt;In this case, it exposed two of them almost immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One was a bug: registration didn't create the actual Gym.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The other was a missing piece of the product: there was no frontend workflow for creating the first sectors.The first one is now fixed.&lt;br&gt;
The second one is next. So, is the MVP ready? Almost.&lt;/p&gt;

&lt;p&gt;And "almost" means something much more concrete now than it did before running this test.&lt;/p&gt;

&lt;p&gt;The core workflow survived. The three user roles behave as expected. Data isolation between gyms works. A completed setting task can travel through the system and become a real Route without manual database work.&lt;/p&gt;

&lt;p&gt;What didn't survive was the onboarding around that workflow.So I'm not adding another major feature yet.&lt;/p&gt;

&lt;p&gt;The next job is much less exciting: let a Gym Manager create and manage sectors without Django Admin. Then I'll delete the test data, create another completely fresh account and do the whole thing again.&lt;/p&gt;

&lt;p&gt;If I can get from registration to a published route without touching the backend manually, then I'll be much more comfortable calling RouteFlow v0.1 usable.&lt;/p&gt;

&lt;p&gt;After that, I want the next bugs to come from people who actually work in climbing gyms. I'm fairly sure they'll find some. That's the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;RouteFlow is still under active development, and the source code is available on &lt;a href="https://github.com/Efowski/routeflow" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the second post about building RouteFlow. In the first one, I wrote about the architecture, gym-level data isolation, the setting workflow and why a completed Setter Task isn't automatically a Route.&lt;/p&gt;

&lt;p&gt;This time I stopped building long enough to actually use the thing.&lt;/p&gt;

&lt;p&gt;Turns out that was useful.&lt;/p&gt;

</description>
      <category>django</category>
      <category>react</category>
      <category>saas</category>
    </item>
    <item>
      <title>RouteFlow — Building a Route Setting SaaS with Django and React</title>
      <dc:creator>Paweł Mazur</dc:creator>
      <pubDate>Tue, 01 Sep 2026 09:59:04 +0000</pubDate>
      <link>https://dev.to/efowski/routeflow-building-a-route-setting-saas-with-django-and-react-40nc</link>
      <guid>https://dev.to/efowski/routeflow-building-a-route-setting-saas-with-django-and-react-40nc</guid>
      <description>&lt;p&gt;Route setting in a climbing gym seems fairly straightforward when you're looking at it from the outside. Old routes come down, new ones go up, somebody grades them and the wall opens again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ajpfrejwcigtckimwzz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ajpfrejwcigtckimwzz.png" alt=" " width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is quite a bit more going on behind the scenes.&lt;/p&gt;

&lt;p&gt;Routes get old. Sectors need regular resets. Setters have to know what they're supposed to build. Someone needs to decide how many new routes are needed and which grades are missing. Then all of that has to be planned around people, dates and the fact that the gym still needs to operate.&lt;/p&gt;

&lt;p&gt;You can manage a lot of this with spreadsheets.&lt;/p&gt;

&lt;p&gt;Up to a point.&lt;/p&gt;

&lt;p&gt;That's basically where RouteFlow started.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm building
&lt;/h2&gt;

&lt;p&gt;RouteFlow is a SaaS application for managing route setting in climbing gyms.&lt;/p&gt;

&lt;p&gt;I'm deliberately focusing on the operational side of the gym rather than building another app for climbers to log their ascents.&lt;/p&gt;

&lt;p&gt;At the centre of RouteFlow is a fairly simple relationship:&lt;/p&gt;

&lt;p&gt;Sector → Setting Session → Setter Tasks → Routes&lt;/p&gt;

&lt;p&gt;A manager or head setter plans work for a sector, decides how many routes should be created and assigns individual tasks to setters.&lt;/p&gt;

&lt;p&gt;The setters work through those tasks, the routes get tested, and finished work can eventually become an actual Route in the gym's database.&lt;/p&gt;

&lt;p&gt;That last part turned out to be more interesting than I initially expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Django on the backend, React on the frontend
&lt;/h2&gt;

&lt;p&gt;The backend is built with Python, Django and Django REST Framework, with PostgreSQL as the database. The application runs in Docker.&lt;/p&gt;

&lt;p&gt;The frontend is React with TypeScript and talks to Django through the REST API.&lt;/p&gt;

&lt;p&gt;There isn't anything particularly exotic about that stack, and that's intentional.&lt;/p&gt;

&lt;p&gt;I'm building RouteFlow as something that should eventually be used by actual climbing gyms. I would rather have boring technology with understandable behaviour than introduce another piece of infrastructure just because I can.&lt;/p&gt;

&lt;h2&gt;
  
  
  A gym has to actually own its data
&lt;/h2&gt;

&lt;p&gt;Once RouteFlow started moving beyond basic CRUD, one of the first things that needed sorting out properly was data ownership.&lt;/p&gt;

&lt;p&gt;The natural boundary is the gym.&lt;/p&gt;

&lt;p&gt;A sector belongs to a gym. Routes inside that sector belong to the same gym. Setting sessions and the tasks created within them need to respect that boundary as well.&lt;/p&gt;

&lt;p&gt;And simply hiding Gym B's data from somebody logged into Gym A wasn't enough.&lt;/p&gt;

&lt;p&gt;The API itself has to enforce it.&lt;/p&gt;

&lt;p&gt;So the Django querysets are scoped to the authenticated user's gym, and I've been testing the API by deliberately trying to retrieve and modify resources belonging to another gym.&lt;/p&gt;

&lt;h2&gt;
  
  
  Those requests return 404.
&lt;/h2&gt;

&lt;p&gt;The same rule applies when creating relationships between objects. For example, a setter belonging to one gym shouldn't be assignable to a setting session in another one.&lt;/p&gt;

&lt;p&gt;This is the kind of work that isn't particularly exciting in a screenshot, but it's much closer to the problems I expect a real SaaS application to have.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setting workflow
&lt;/h2&gt;

&lt;p&gt;A Setting Session describes a planned reset or setting operation for a sector.&lt;/p&gt;

&lt;p&gt;It contains things such as the date, lead setter and target number of routes. From there, individual Setter Tasks can be created and assigned.&lt;/p&gt;

&lt;p&gt;A task currently moves through:&lt;/p&gt;

&lt;p&gt;Todo → In Progress → Testing → Done&lt;/p&gt;

&lt;p&gt;It can also contain a target grade, hold colour, setter and due date.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqmrdg6e42dtyqfgb3j6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqmrdg6e42dtyqfgb3j6h.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
Originally, getting a task to Done looked like the natural end of that workflow.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;A finished task means the setter has finished the work. It doesn't necessarily mean RouteFlow has a new Route in its database.&lt;/p&gt;

&lt;p&gt;So publishing became a separate backend operation.&lt;/p&gt;

&lt;p&gt;Once a completed task is published, Django creates the Route and links it back to the original Setter Task. The operation is atomic, so if part of that process fails, it doesn't leave half-created data behind.&lt;/p&gt;

&lt;p&gt;It also means the same task can't accidentally produce two routes.&lt;/p&gt;

&lt;p&gt;That's a relatively small feature from the user's point of view — essentially an action in the interface — but it forced me to think much more carefully about where one part of the workflow actually ends and another begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I built the dashboard
&lt;/h2&gt;

&lt;p&gt;The most recent part of RouteFlow is the operational dashboard.&lt;/p&gt;

&lt;p&gt;By this stage the application already knew about setting sessions, tasks and published routes. The problem was that knowing something and making that information useful are two different things.&lt;/p&gt;

&lt;p&gt;I wanted the dashboard to answer fairly mundane but important questions:&lt;/p&gt;

&lt;p&gt;What's being set right now?&lt;/p&gt;

&lt;p&gt;How far along is it?&lt;/p&gt;

&lt;p&gt;What's coming next?&lt;/p&gt;

&lt;p&gt;Is anything late?&lt;/p&gt;

&lt;p&gt;Are there enough tasks assigned to actually hit the planned number of routes?&lt;/p&gt;

&lt;p&gt;So an active session now shows its sector, lead setter, planned date, target route count and the current state of its tasks.&lt;/p&gt;

&lt;p&gt;Task progress is split between Todo, In Progress, Testing and Done.&lt;/p&gt;

&lt;p&gt;The dashboard also compares the target number of routes with the number actually published.&lt;/p&gt;

&lt;p&gt;That distinction caused one small change in how I thought about completion.&lt;/p&gt;

&lt;p&gt;At first it would have been easy to calculate session progress from tasks marked Done. But a done task isn't necessarily a published route.&lt;/p&gt;

&lt;p&gt;If a session is supposed to produce 10 routes and six tasks are done but only four routes have actually been published, calling the session 60% complete would be misleading.&lt;/p&gt;

&lt;p&gt;So RouteFlow considers it 40% complete.&lt;/p&gt;

&lt;p&gt;It sounds like a minor detail, but those are exactly the details that start appearing once separate features have to work together as one system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that need attention
&lt;/h2&gt;

&lt;p&gt;The dashboard also has an Attention Required section.&lt;/p&gt;

&lt;p&gt;Currently it catches things such as overdue tasks, overdue setting sessions and sessions where the number of assigned tasks is lower than the target number of routes.&lt;/p&gt;

&lt;p&gt;I actually considered adding an alert for sessions without a lead setter while building this.&lt;/p&gt;

&lt;p&gt;Then I realised the application already requires a lead setter when creating a session.&lt;/p&gt;

&lt;p&gt;There was no point adding dashboard logic for a state the system doesn't allow to exist.&lt;/p&gt;

&lt;p&gt;Removing that check was probably more useful than adding another dashboard card.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it is now
&lt;/h2&gt;

&lt;p&gt;RouteFlow isn't finished, and I don't want to present it as if it is.&lt;/p&gt;

&lt;p&gt;What works now is the core operational chain:&lt;/p&gt;

&lt;p&gt;Setting Session → Setter Tasks → Task Workflow → Route Publishing → Operational Dashboard&lt;/p&gt;

&lt;p&gt;The next milestone is mostly cleanup.&lt;/p&gt;

&lt;p&gt;There is still some demo and fallback data left from earlier stages of frontend development. I'm removing that and checking the application screen by screen to make sure the API is the actual source of truth everywhere.&lt;/p&gt;

&lt;p&gt;After that I want to run the whole thing the way a new gym would: start with an empty account, create the gym structure, add setters and routes, plan a session, assign the work, publish the resulting routes and see whether anything requires developer intervention along the way.&lt;/p&gt;

&lt;p&gt;If it does, v0.1 isn't ready yet.&lt;/p&gt;

&lt;p&gt;Once that works, the useful part starts: putting RouteFlow in front of people who actually run climbing gyms and seeing which assumptions were right and which ones weren't.&lt;/p&gt;

&lt;p&gt;For me, RouteFlow is also a good example of the kind of development work I want to do more of: Django, REST APIs and applications where the interesting problems aren't just about displaying data, but about defining what should happen to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;RouteFlow is still under active development, but if you'd like to take a look at the project itself, the source code is available on GitHub:&lt;a href="https://github.com/Efowski/routeflow" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll be sharing more of the technical decisions, mistakes and lessons from building RouteFlow as the project moves toward its first usable release.&lt;/p&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>react</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
