Pilot Training Diary

Days 0, 1 and 2

Here is my story of how I was building my Pilot Training app, which is my journey back to programming. This time with the use of AI.

You can view the latest version at https://pilot-training.vercel.app

The title is 0, 1 and 2 because I just thought recently I’d take you on this journey with me, and I’m on something like Day 3. Day 0 is setting up the environment.

So, what did I want to do, and where did I start.

I have Chat GPT Plus, I asked it to help me build at app, guide me by the hand and that I want to learn as we go. Chat suggested Cursor, Next.js, React, TypeScript, Supabase and Vercel to host it. I decided to keep my documentation in Obsidian. Before building the first screen I asked for a design system and based on the description of my app, Chat suggested MUI. Most of this stuff, apart from Cursor, I hadn’t touched before. Everything was new.

I’m not going to go into details how to do that, because just ask Chat.

I know how to design software, I may not be great with technology, but the logical components are not Greek to me. I started developing the full stack from the very start. Every object was displaying data directly from the data base and from the very beginning in the correct UI components.

The app is about training pilots. I want it to be scalable, flexible and most of all, meet all the complexities of pilot training such as different types of learning artefacts, one-off and recurring activities and qualification expiration dates.

From my 20 years in IT I have seen tonnes of technical debt and lack of documentation (as if there was an inverse relationship between the two…), so I asked Chat to regularly create documents and document architectural decisions (ADRs) and refactor as we go, when it makes sense and create backlog items when it’s ok to put it off.

I also wanted it to run on production from the very beginning, so I started with creating a user in the database and giving them a login screen to start.

It’s helping me diagnose problems. Debugging has never been so easy.

And it’s a learning style that I really enjoy. I literally go over the code it produces and ask, or explain what each line means, and it’s infinitely patient with me.

The App

Like I said, from the very beginning the app was productive, it was also built with reusable components from a design system and it was responsive.

I first built a master-detail view of imaginary pilots and imaginary course, and when I realised that this might be a repeating pattern, I said „Hey Chat, let’s create a generic master-detail component, where we’ll just say where to take the data from”.

And it told me exactly what to do.

You might wonder „what do you mean, it told you what to do, and not 'did it'”?

Because I’m working with Chat GPT, not a model built into Cursor. Chat tells me what file to update and it gives me the code, but cannot access the sources itself. That’s fine, because I get to learn.

Day 3

Continuing my journey on building an app with AI…

Now that most of my infrastructure work is done (I realised I still don’t have a notification mechanism in place – will address that soon), I am at what I love doing most – defining the domain!

The app is about training pilots, but learning is learning and a curriculum is a curriculum. So, with my buddy Chat, we’re working on the Curriculum subdomain.

It evolved from having everything, such as Curriculum and an individual learning path and knowledge assessments and finally the rank a pilot will have achieved when they pass all their exams, in one place, to really breaking it down in a way that the blocks are smaller, contain only pieces relevant to the flow – or context – and they relate to each other, rather than belonging in one another.

Here’s what it means in practice and how it differs from mere UI design.

On the UI, you see a list of pilots, you click on one, go to the pilot’s details page. There you see what rank they are and what training they have scheduled. When you click on the training, you can see what modules and lessons they need to take, there is also the training history, where you can see when they passed their exams.
It seems like everything belongs to the pilot.

In fact, it’s almost upside down. The training plan is actually 2 things: The content of the Curriculum with assigned dates. The dates are based on the duration of the lessons. There are Exams that must be taken, Verification mechanism that determine whether the answer is correct or not and a Threshold that determines if the Exam was passed and the Qualification that leads to the Rank can be achieved.
So, what you would see in a single side panel, perhaps under 2 tabs – Training Plan and Training History – is a quite complicated structure accounting for initial training, recurring training, validity and even content version.

As Albert Einstein used to say „Everything should be made as simple as possible, but not simpler.”

I need a structure, that will ensure data consistency, handle edge cases and allow to easy scaling in the future, but I also don’t want to overcomplicated just yet. I’m looking for a nice balance of foundation where, if needed, refactoring can be done without pain.

I don’t trust Chat blindly, but it’s being very helpful and we’re having some really good discussions.

Day 4

The next day, we had a domain modelling session.

It started with a question „What if I want to change one thing in a lesson. Will I be creating an entire Curriculum Version and copying everything?”

And sometimes Chat provides very direct responses but with a little back and forth, we arrived at a very flexible model that allows us to revise lessons and modules and curricula and build only on the latest versions of individual building blocks.

For example, I have have the first version of a Curriculum, with first versions of every component and every lesson, and I can have a second version of the same Curriculum, with only one lesson upgraded to version 2. Everything that remains in version 1, will be reused, and not copied. I know this can turn into spaghetti, but since refactoring is so fast and easy with AI, I’m not worried at the moment.

My structure is also flexible. I can reuse lessons across modules, modules across curricula or any combination.

Chat prepares SQL statements and sample data, I store copies of everything in Obsidian, for future use.

Supabase schema after day 4 as discussed with Chat and built by pasting Create Table statements.

And, as it often happens, I published with a bug and learned an important lesson. I finally understood the difference between .tsx files and .ts files!

Using a double slash // for comments doesn’t work in .tsx, because .tsx is like HTML back in the day, and HTML used /* your comment goes here */


Day 5

Today I finally got the gist of proper DDD end to end in web apps. Until now, I was only familiar with strategic DDD. How do you model the domain, so that it makes sense. There is also tactical DDD which talks about how do you structure your code, so that it satisfies the principles. The need came, when I wanted to display the content of a Curriculum once selected. As long as all the content resided in one table, that was easy. But in my app, a Curriculum is a collection of Modules which is a collection of Lessons, everything is versioned and can be mixed and matched in all ways imaginable.

Not long ago, I used to think, that this is how it worked. And it still sort of is, however, what if, one day, I wanted to switch to a different database? Or, I would like to use a different front end technology? I would need to make changes to at least 2 of the 3 layers.

Web developers know this, but for me, this was the first hands on experience of building a full stack flow the proper way.

Instead of 3, there are now 6 layers and today, I got really frustrated trying to understand what is happening, and when I complained to Chat about it, it came with a helping hand

However, trying to create a sequence diagram of all of this, I found out about Mermaid. An AI diagramming tool.

You can even talk to it!

My Pilot Training app, which by now has a new name – Cadette – is still pretty simple, but what you’re looking at is a Curriculum details panel, which displays a tree of Modules and their Lessons.

The fun stuff is that each one of those objects: the Curriculum, the Module and the Lesson can have multiple versions, and be assigned in all sorts of ways and orders.

So, the following scenarios are possible:

Assuming that a Lesson or a Module with the same ID is THE SAME object, I can assign them to multiple curricula, and in a different order each time. (The purple background indicates changes. )

Perhaps this is superfluous right now, but I am learning and such flexibility is desirable in complex environments, so, even if in real life, nobody would ever need such reuse of learning material, it might apply to other objects, such as students and courses and classrooms and flight simulators.

Next, I want to start building the Pilots’s Learning Plan.

Day 7

The last few days have been around refactoring code, defining subtle changes in the domain and updating documentation.

The first think I did not like is that the details panel title looked differently on the desktop and mobile views. Notice that on the desktop, the title is on the same panel as the content, whereas on the mobile it’s above the panel. I wanted the same look on both page.

I started refactoring with Chat GPT, following what changes it’s making. We fixed the backgrounds, but I found that the code confusing and repetitive. There were too many „if is phone” statements and too many „returns”. As s rule of thumb, avoid repeating code, it’s going to bite in the future.

I came to the conclusion that the logic is:

  • On the phone:
    • if nothing is selected, show just the master list
    • if an item is selected, hide the master list, show the back button

I now have a simplified master detail template ready to be reused for any future views and both desktop and mobile views look very similar. I also hid less prominent details under an info button.

Curse content view on desktop

It’s quite challenging to find a good balance between refactoring code and moving on, but now I’m ready.

I was wondering should I proceed with the Pilot use cases, which would be building a learning plan or should I continue the Administrator path which means modifying content.

I decided on the latter, because I want to dig deeper into DDD principles and CQRS is the next topic I want to explore.

CQRS stands for Command Query Responsibility Segregation and simply it means that in code you split reading data from modifying it. Split as in write the logic in different files. This is the next step on my Full Stack Developer journey and I am very excited!

Until this happened…

I realised that my login screen displays the primary navigation drawer…

One step ahead, 2 steps back. That’s what this feels like sometimes.

Day 8

Today I decided to refactor the UI. I have been playing with updating the Design System defaults, which I think I regret already, but furthermore, I decided to get rid of the App Bar, which I was holding for I’m not sure what.

See the empty space at the top of the screen? It’s going away. The Courses list and the Course Detail panel are going all the way up.

I’m doing this, because the next step is the Lesson Content page.

I wanted the contents of a Lesson to be on their own, separate full screen page, rather than a dialog or an overlay, because it’s a complex piece of business logic and design and requires as much screen real estate as I can give it. But primarily, I imagine that in the future, I might need a Dialog on this page or some other user interaction and doing that on top of a Dialog is evil.

I discussed this with my buddy Chat, and it proposed this structure:

Whatever masters, details and content I will have, I can „simply” inject it in one of the containers of the wrapping layout.

This approach also simplifies the proper use of elevation and shadows. It’s something I still have to fix (some experiments go rogue), but it will be easy to follow instruction from now on.

Day 9

Just as I was about to build the first „Update” use case – update the content of a lesson – I could not resist some more refactoring and it was back to Figma! By the time I explained to Chat what I wanted, sketching it out, was so much simpler:

Desktop version
Mobile version

Each one of those elements will have their own generic component eventually, to ensure consistency.

Today’s focus is on the Toolbar and the „Edit” button in its place.


The latest version of the app is always available here https://pilot-training.vercel.app/courses

login: pilot@bestpilots.fly
password: bestpilots4ever!