DEV Community

Cover image for πŸš› After 100 Hours of Learning, a Truck Driver Found and Fixed 9 Loading Mistakes in His Flask Code
tosane932
tosane932

Posted on

πŸš› After 100 Hours of Learning, a Truck Driver Found and Fixed 9 Loading Mistakes in His Flask Code

About Me

Hello from Japan! πŸ‡―πŸ‡΅

I am a professional truck driver with seven years of experience in logistics and no professional IT background.

On May 12, 2026, I installed Linux on a 13-year-old laptop and began teaching myself Python.

After approximately 100 hours of independent study, I decided to stop building new features for a moment and review the system I had already created.

This article explains what I found.

GitHub Profile πŸ‘ˆ

Latest Demo Video πŸ‘ˆ


Why I Reviewed My Code

I did not want to build something once and then abandon it.

Instead of producing many shallow projects, I wanted to continue improving one system until it became genuinely usable.

I take the same approach with my Qiita articles.

When I find a typo, unclear sentence, or incorrect explanation, I revise it.

I edited my article about Mini 4WD and Python three or four times.

I believe code should be treated the same way.

In my daily work as a truck driver, I often stop and ask myself:

Is this load really safe enough for departure?

That same cautious habit appears when I write code.

Many workplace accidents are caused by assumption-based thinking:

The other vehicle will probably stop.

It will probably be fine.

In Japanese driving safety education, this way of thinking is sometimes called darou drivingβ€”driving based on the assumption that everything will probably be fine.

The safer approach is kamoshirenai drivingβ€”driving while considering what might go wrong.

For example:

  • Check behind the truck while reversing
  • Get out and inspect unfamiliar surroundings directly
  • Leave enough distance between vehicles
  • Secure products so they cannot move
  • Confirm the load before departure

These precautions allow us to prevent accidents before they happen.

I realized that coding works the same way.

  • β€œIt is running, so it is probably fine.”

    β†’ debug=True was still enabled.

  • β€œI do not use this import, but leaving it should probably be fine.”

    β†’ Unnecessary imports accumulated.

  • β€œThe input will probably be valid.”

    β†’ Input validation was missing.

Assumption-driven coding creates loading mistakes.

Defensive coding can prevent many of them.

I already understood this principle from logistics work.

Safety inspection illustration

How do we prevent serious accidents at work?

A major accident is often preceded by many small mistakes.

Examples include:

  • Forgetting to load or unload an item
  • Loading the wrong product
  • Damaging merchandise

When these happen individually, recovery may be relatively quick.

However, a vehicle accident is completely different.

Recovery may require:

  • Confirming everyone's safety
  • Moving the vehicle
  • Contacting the police
  • Exchanging information
  • Reloading the cargo
  • Reporting delays
  • Explaining responsibility to customers and the company

The recovery cost becomes enormous.

It can become both a liability issue and a trust issue.

Software is similar.

Leaving debug=True enabled, keeping unused imports, or forgetting validation may look like small problems.

However, when several small issues overlap in production, the recovery cost can increase dramatically.

That is why I wanted to eliminate small mistakes before they combined into a serious failure.

Perfect systems do not exist.

The environment must always be reviewed and updated.

A truck does not become permanently safe simply because it passed an inspection.

A bolt may have loosened.

A repair may not have been completed correctly.

That is why drivers inspect their vehicles every day and perform double checks.

Code is the same.

The moment you believe a system is complete may be the most dangerous moment.


Truck loading image

Photo by Graficon Stuff on Unsplash

Loading Mistakes 1–6: Problems Found During My First Review

Loading Mistake 1: A Function Was Left Behind

A function named build_sales_prompt existed in both app.py and prompts.py.

The version in prompts.py was not being used by anything.

Cause: Cargo Left Behind After Unloading

I felt satisfied after moving the function into prompts.py, but I forgot to delete the old version from app.py.

My thinking was:

I copied it, and the application still works, so it is probably fine.

This was assumption-driven coding.

Countermeasure: Inspect the Cargo Area After Unloading

When moving code, I now check both sides of the change:

  • Was the old code removed?
  • Was the application switched to the new module?
  • Are there any duplicate definitions?
  • Is the new import actually being used?

This is similar to checking that the truck bed is empty after unloading.

# Before:
# The same function also existed in app.py.

def build_sales_prompt(sales_summary: str) -> str:
    return f"..."

# After:
# app.py only imports the function from prompts.py.

from prompts import build_sales_prompt
Enter fullscreen mode Exit fullscreen mode

Loading Mistake 2: I Forgot to Clean Up the Testing Area

A file named js_test.html remained inside the production templates directory.

I had created it to practice asynchronous JavaScript communication.

It was an experimental file that did not belong in the production application.

Cause: My Attention Moved to the Next Task

I created the experimental file inside the production folder because I wanted to test the feature quickly.

Once the asynchronous communication worked, I felt relieved and immediately moved on to the next task.

The experiment had succeeded, but I forgot to clean up the testing area.

Countermeasure: Remove Experimental Files Immediately

When an experimental file is no longer needed, I delete it immediately.

I also avoid creating temporary files directly inside production folders.

The important lesson is simple:

Finishing an experiment includes cleaning up after it.


Loading Mistake 3: Debug Mode Was Still Enabled

The application was close to being published with:

app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

When debug mode is enabled and an error occurs, internal code and sensitive system information may be displayed in the browser.

This was the most dangerous mistake in the review.

Cause: It Had Become a Standard Phrase

During development, debug=True was extremely useful because Flask displayed detailed error information.

Over time, this line became something I typed automatically:

app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

It felt like a normal part of starting the application.

I completely forgot that debug mode must be disabled in production.

What I Thought When I Noticed It

I realized:

If someone had used the application and triggered an error, the internal structure of the system could have been exposed.

That thought genuinely frightened me.

Countermeasure: Do Not Depend on Human Memory

Instead of relying on myself to remember to remove debug=True, I changed the application so the setting is controlled by an environment variable.

In production, debug mode is disabled automatically.

# Before

app.run(debug=True, host="0.0.0.0", port=5000)

# After

debug_mode = (
    os.environ.get("FLASK_DEBUG", "false").lower() == "true"
)

app.run(
    debug=debug_mode,
    host="0.0.0.0",
    port=5000
)
Enter fullscreen mode Exit fullscreen mode

This changed the design from:

I must remember to disable it.

to:

The system is safe by default.


Loading Mistake 4: Old and New Systems Were Both Loaded

Even after migrating to PostgreSQL, the old Excel-writing logic remained in the application.

This created a situation where data could exist in two different places.

If Excel and PostgreSQL contained different values, it would become difficult to determine which source was correct.

Cause: I Kept the Old Code as a Safety Blanket

I was afraid to delete the Excel logic because it had worked before.

Once PostgreSQL started working, I felt relieved.

However, I postponed the final cleanup step.

I treated the old code like a safety measure, even though it was no longer needed.

Countermeasure: Trust Git and Delete Obsolete Code

Version control allows old code to be restored when necessary.

Obsolete code does not need to remain in the current project.

It is not a safety blanket.

It is unnecessary cargo.

After completing a migration, I should remove the old implementation without hesitation.

# Before:
# Excel processing and database processing both remained.

generate_excel(year, month, products_data)  # Removed
get_filtered_sales_data(
    target_year,
    target_month
)  # Removed

# After:
# PostgreSQL became the single source of data.

_get_sales_from_db(target_year, target_month)
Enter fullscreen mode Exit fullscreen mode

Loading Mistake 5: The Year Selection Had an Expiration Date

The year options in dashboard.html were hard-coded:

  • 2025
  • 2026
  • 2027

The application would require a manual code update as soon as 2028 arrived.

Cause: I Focused Only on the Present

During development, the application worked perfectly as long as it displayed the years 2025 through 2027.

I did not consider maintainability or ask:

What happens when the year changes?

My thinking stopped at:

It works now, so it is fine.

Countermeasure: Generate Years Dynamically

I changed the system to retrieve the current year in Python and generate the year options using a Jinja2 loop.

The application no longer requires a manual update every January.

<!-- Before: hard-coded years -->

<option value="2025">2025</option>
<option value="2026">2026</option>
<option value="2027">2027</option>

<!-- After: dynamically generated years -->

{% set current_year = now.year %}

{% for y in range(current_year + 1, current_year - 3, -1) %}
<option value="{{ y }}">{{ y }}</option>
{% endfor %}
Enter fullscreen mode Exit fullscreen mode

This changed the feature from a short-lived implementation into a more maintainable one.


Loading Mistake 6: Unnecessary Dependencies Were Still Loaded

The requirements.txt file contained both the old and new Google AI libraries.

Keeping unused libraries increases installation time and may introduce version conflicts.

Cause: I Exported Everything with pip freeze

While implementing the AI feature, I tested several libraries.

After finding the correct one, I used:

pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

This exported every installed package, including old libraries used only during experimentation.

Countermeasure: Uninstall Unused Packages Before Exporting

When replacing a library, I now remove the old one with pip uninstall before updating requirements.txt.

After adding cargo, you must also confirm whether anything unnecessary should be unloaded.

# Before:
# Both old and new libraries were included.

google-genai==2.4.0
google-generativeai==0.8.6  # Unnecessary

# After:
# Only the required library remains.

google-genai==2.4.0
Enter fullscreen mode Exit fullscreen mode

Warehouse image

Photo by Alghogy on Unsplash

Loading Mistakes 7–9: Problems Identified with Gemini

Loading Mistake 7: I Kept an Old File Because I Was Attached to It

After the PostgreSQL migration, the application no longer needed to aggregate data from Excel.

However, auto_aggregator.py was still sitting inside the project folder.

This was the file-level version of Loading Mistake 4.

An unused artifact from an earlier version of the project remained in the active codebase.

Cause: Sunk Cost and Emotional Attachment

I had worked hard to create this file before introducing the database.

It had actually worked.

I understood logically that it was no longer needed.

However, emotionally, I felt:

I spent so much time writing this. I should keep it just in case.

As a result, I left it sleeping in a corner of the project.

Countermeasure: Store History in Git and Keep Only Active Code

Git already preserves the history of old code.

The current working directory should contain only what the current application needs.

Past code belongs in version history, not in the active project structure.


Loading Mistake 8: The Application Accepted Cargo Without Inspection

The daily sales form accepted negative numbers and invalid strings.

Those values could reach the database and cause:

  • Incorrect sales totals
  • Unexpected exceptions
  • Corrupted or inconsistent records

Cause: I Tested Only According to My Own Rules

Because I created the form, I entered only valid numbers during testing.

I did not imagine that another user might:

  • Enter a negative number
  • Submit an empty value
  • Send invalid text
  • Bypass browser-side restrictions

This was a classic example of assumption-driven coding.

Countermeasure: Use Two Inspection Layers

A factory should never allow contaminated raw materials to enter the production line.

I applied the same idea to input validation.

The first inspection layer is in HTML:

  • min="0" prevents negative numbers
  • required prevents empty submission

The second inspection layer is in Python:

  • Values are converted and checked again
  • Invalid data is rejected before reaching the database
# After:
# Two-layer validation

try:
    qty_int = int(float(quantity))
except (TypeError, ValueError):
    continue

if qty_int < 0:
    continue
Enter fullscreen mode Exit fullscreen mode

Browser-side validation improves usability.

Server-side validation protects the system.

Both are necessary.


Loading Mistake 9: There Was No Pre-Departure Load Check

The application did not verify whether required environment variables, such as the API key, existed during startup.

The application could start successfully even when important settings were missing.

The fatal error would only appear after a user pressed a button.

Cause: My Own Environment Always Worked

Once I configured environment variables on my computer, the application continued working.

I did not sufficiently consider other environments:

  • Another developer's computer
  • A production server
  • A newly created container
  • A fresh deployment
  • A machine without the .env configuration

I assumed the configuration would always exist.

Countermeasure: Perform a Pre-Departure Check

Before a truck departs, the driver confirms that the cargo is loaded correctly.

The application should do the same.

I added a startup check that verifies all required environment variables immediately.

When even one variable is missing, the application stops before serving users.

# Fail fast:
# Check required environment variables at startup.

_required_env_vars = ["GEMINI_API_KEY"]

_missing = [
    var
    for var in _required_env_vars
    if not os.environ.get(var)
]

if _missing:
    raise RuntimeError(
        "Missing required environment variables: "
        + ", ".join(_missing)
    )
Enter fullscreen mode Exit fullscreen mode

The design changed from:

Discover the problem after a user triggers it.

to:

Detect the problem immediately during startup.

This is an example of the Fail-Fast principle.

Logistics image

Photo by Alghogy on Unsplash


What Changed After Fixing the Nine Problems

Assumption-driven thinking began to disappear from my code.

I added or improved:

  • Input validation
  • Fail-Fast startup checks
  • Environment-based debug settings
  • Removal of unused files
  • Removal of obsolete dependencies
  • Database-only data management
  • Dynamic year generation
  • Clearer module separation
  • Safer default behavior

After fixing these nine issues, the application became better at protecting itself.

Even when unexpected data arrives or a configuration value is missing, the system now responds more safely.

The goal changed from:

It works.

to:

It is harder to break, even when someone else uses it.

For seven years, I have practiced defensive driving by thinking about what might happen.

After 100 hours of programming study, I finally began expressing that mindset in code.


Next Step: Dockerization

Even after fixing the nine loading mistakes, one major problem remained:

The application works only in my environment.

Differences in environments can cause new failures:

  • Different Python versions
  • Different dependency versions
  • Different PostgreSQL settings
  • Missing environment variables
  • Different operating systems

The common problem is:

It worked on my computer.

This is another gap created by assumption-driven development.

Docker can package the application environment, including:

  • Python
  • Flask
  • PostgreSQL-related dependencies
  • Application libraries
  • Runtime configuration

With the same container definition, the application can run in a consistent environment on different machines.

The next stage is to evolve the project from:

A system that works only on my computer

into:

A product that can be shipped and reproduced anywhere

In logistics terms, Docker is the container that allows the product to be transported safely without changing what is inside.


GitHub Repository: sales_data_app

sales_data_app screenshot

Latest YouTube Demo Video πŸ‘ˆ

Latest version: Version 2


sales_data_app Maintenance Series

Pre-Production Inspection Trilogy

Post-Deployment Maintenance Series

🍞 sales_data_app on GitHub

Top comments (0)