DEV Community

Cover image for Day 10 to 13 - From Static Portfolio to Real System
Sushant Gaurav
Sushant Gaurav

Posted on

Day 10 to 13 - From Static Portfolio to Real System

Until Phase 3, everything lived inside the browser.

The frontend was clean.
The architecture was scalable.
The product felt complete.

But technically?

It was still self-contained.

No server.
No APIs.
No system boundaries.
No production-level backend thinking.

Phase 4 changed that.

This is where the project stopped being a polished frontend
and started becoming a real full-stack system.

Day 10 – Laying the Backend Foundation the Right Way

Most developers, when adding a backend to a portfolio, do this:

  • Install Flask
  • Create app.py
  • Add some routes
  • Call it done

I didn’t want that.

Because this backend wasn’t just about serving JSON.
It was about building something that looks and behaves like a production service.

1. Starting with Structure, Not Routes

The first thing I did was create the backend using uv and properly structure the environment.

Not just a folder.
A real backend project.

That meant:

  • Virtual environment management
  • Proper dependency installation
  • Structured directory layout

Before writing a single route.

Because architecture begins before code.

2. Environment Configuration – Thinking Beyond Localhost

Instead of a single .env file, I created:

  • .env.production
  • .env.development
  • .env.testing

Why?

Because real systems don’t run in one mode.

They run in:

  • Local development
  • CI/testing
  • Production deployment

And each environment:

  • Has different secrets
  • Different debug levels
  • Different logging behaviour
  • Different security rules

This decision alone separates:

hobby backend
from
deployable backend

3. Configuration as Code (settings.py)

I created app/config/settings.py and defined configuration classes.

Not just variables.
Classes.

Why?

Flask supports environment-based configuration through object-based loading.

So instead of:

app.config['DEBUG'] = True
Enter fullscreen mode Exit fullscreen mode

I built structured config classes like:

  • DevelopmentConfig
  • ProductionConfig
  • TestingConfig

This gives:

  • Clear separation of concerns
  • Centralised configuration
  • Easy scalability
  • Cleaner environment switching

This is how backend systems scale safely.

4. The Application Factory Pattern

Instead of writing:

app = Flask(__name__)
Enter fullscreen mode Exit fullscreen mode

I implemented:

def create_app():
    ...
Enter fullscreen mode Exit fullscreen mode

This is called the Application Factory Pattern.

And it’s critical for:

  • Testing
  • Extension initialisation
  • Clean separation of app creation
  • Avoiding circular imports
  • Multi-instance setups

This is how larger Flask systems are built.

Not in tutorials — but in real services.

5. Extensions Layer

I created:

app/extensions.py
Enter fullscreen mode Exit fullscreen mode

And registered all extensions there.

Why isolate extensions?

Because:

  • It prevents circular dependencies
  • Keeps __init__.py clean
  • Makes the system modular
  • Allows independent testing

Again — small decision, big scalability impact.

6. Blueprint-Based Routing

Instead of dumping routes in one file, I designed:

route_blueprints

This allows:

  • Separation by domain (projects, contact, health, etc.)
  • Modular expansion
  • Cleaner code ownership
  • API versioning later if needed

You don’t build blueprints for 3 routes.

You build blueprints when you expect growth.

And I expect growth.

Day 11 – Turning Architecture into Real APIs

Day 10 built the system.

Day 11 made it useful.

1. Creating Real Routes

I implemented routes for:

  • Projects
  • Skills
  • Achievements
  • Health
  • Contact

Now the frontend no longer depends on local JSON.

The backend now exposes structured APIs.

Which means:

The architecture decision from Day 7 (JSON-first design)
paid off.

Because now:

JSON → API
Same shape
Zero UI rewrite

That’s intentional design maturity.

2. Moving Frontend Data into Backend

I migrated all frontend data into:

api/data/
Enter fullscreen mode Exit fullscreen mode

This was a psychological shift.

Now the backend is the source of truth.

The frontend becomes a consumer.

That’s real system ownership.

3. Introducing Pydantic – Validation Done Properly

Most Flask tutorials validate data manually.

I introduced Pydantic models.

Why?

Because:

  • Strong validation
  • Type enforcement
  • Cleaner error messages
  • Self-documenting schemas

Now the Contact route, for example:

  • Doesn’t just accept any JSON
  • It validates the structure
  • It enforces required fields
  • It ensures data integrity

That’s how production APIs behave.

4. Logging and Error Handling

Instead of printing errors or returning raw messages:

I implemented:

  • Structured logging
  • Centralised error handling
  • Clean response formats

This is important for:

  • Debugging production
  • Monitoring
  • Observability
  • Reliability

It also signals something important:

This backend is meant to run in the real world.

5. Finalising the Contact Route

The Contact route is now:

  • Validated via Pydantic
  • Logged
  • Error-handled
  • Structured in blueprint
  • Ready for future email service integration

It’s no longer:

a form endpoint

It’s:

a system boundary.

Flask web app architecture flowchart

Why Day 10 & 11 Matter

Most developers build the backend as an afterthought.

I treated it as:

  • an architecture layer,
  • a deployment-ready service,
  • a long-term foundation.

By Day 11, the system had:

  • Config abstraction
  • Environment separation
  • App factory pattern
  • Modular blueprints
  • Data validation
  • Structured logging
  • Proper error handling

That’s not a portfolio backend.

That’s real backend engineering.

Day 12 – Observability, Discipline, and Production Thinking

If Day 10 was architecture
and Day 11 was functionality,

Then Day 12 was about something most developers ignore:

Observability.

Because building APIs is easy.
Running APIs reliably is not.

1. Extracting Logging into Its Own Layer

I created:

  • app/utils/logger.py
  • app/utils/__init__.py
  • app/utils/error_handler.py

Why isolate logging?

Because logging is not a feature.
It’s infrastructure.

By extracting it:

  • Routes remain clean.
  • Business logic is not polluted.
  • Logging behaviour can evolve independently.
  • Production formatting can differ from development formatting.

This is what happens in mature systems:
Logging becomes a service layer.

2. Centralised Error Handling

Instead of handling errors inside each route:

try:
    ...
except Exception as e:
    ...
Enter fullscreen mode Exit fullscreen mode

I introduced centralised error handling.

Why?

Because error behaviour should be consistent across the entire API.

Now:

  • All exceptions follow structured responses.
  • All failures are logged properly.
  • The API has predictable failure formats.

That predictability matters when:

  • Frontend integrates deeply
  • Monitoring tools are added
  • Alerts are configured
  • Logs are parsed automatically

This is the difference between:

“It works.”

and

“It’s production-ready.”

3. Adding Logging to Every Route

On Day 12, I went back and integrated logging into:

  • Contact route
  • Contact validation logic
  • Projects route
  • All remaining endpoints

Not because it was required.
But because consistency matters.

Every request now:

  • Leaves a trace.
  • Records meaningful information.
  • Fails loudly (in logs), not silently.

This is the kind of invisible work that makes systems maintainable long-term.

And this is exactly the kind of discipline most portfolio backends skip.

Day 13 – Closure, Review, and Merge

Day 13 was not about new code.

It was about maturity.

1. Final Route Review

Before merging, I reviewed:

  • Route structure
  • Blueprint registrations
  • Logging consistency
  • Validation integrity
  • Response formats
  • Naming conventions

This is what tech leads do before release.

Not because something is broken.
But because once merged, it becomes baseline architecture.

2. Pushing, Merging, and Closing the Branch

This might sound trivial.

But merging the backend branch symbolised something important:

The system is no longer frontend-only.
It is now officially full-stack.

The merge wasn’t just code integration.

It was an architectural consolidation.

What Phase 4 Actually Achieved

By the end of Day 13, the project now has:

1. Structured Backend Architecture

  • Application Factory Pattern
  • Environment-based configuration
  • Blueprint modular routing
  • Extensions isolation

2. Validation Layer

  • Pydantic schemas
  • Structured request validation
  • Clean error responses

3. Observability

  • Centralised logger
  • Structured logs
  • Error handler layer
  • Route-level logging integration

4. Clean Separation of Concerns

Frontend:

  • UI rendering
  • Data consumption

Backend:

  • Data ownership
  • Validation
  • API contracts
  • System boundary

That separation is huge.

Because now the project is not a React app with some Python attached.

It’s:

A client-server system with clear responsibilities.

The Bigger Shift: From Developer to System Thinker

Phase 3 made me think like a frontend architect.

Phase 4 made me think like a systems engineer.

Because now I had to consider:

  • Configuration management
  • Environment isolation
  • Validation enforcement
  • Error standardisation
  • Logging discipline
  • Modularity
  • Future scaling

This is no longer “I built a portfolio”.

This is:

I designed and implemented a maintainable, extensible, observable full-stack system.

And that difference shows in code.

The Real Outcome of Day 10–13

This backend is not overengineered.

It’s intentionally structured.

If tomorrow I decide to:

  • Add authentication
  • Add database persistence
  • Add rate limiting
  • Deploy to production
  • Add monitoring tools
  • Integrate CI/CD

The foundation is already ready.

That’s what good architecture does.

It prepares you for change before change arrives.

Closing Phase 4

By the end of Day 13:

The project is no longer:

  • A static portfolio
  • A frontend showcase
  • A React experiment

It is now:

  • Structured
  • Layered
  • Observable
  • Deployable
  • Extendable

It behaves like a real product.

And that was always the goal.

If you’re following along, the complete source lives here:
👉 GitHub Repository: Portfolio.

Top comments (0)