DEV Community

Cover image for From Prototype to Polished: Reviving My Dart Personal Blog with GitHub Copilot
Alain Chan
Alain Chan

Posted on

From Prototype to Polished: Reviving My Dart Personal Blog with GitHub Copilot

GitHub “Finish-Up-A-Thon” Challenge Submission

This is a submission for the GitHub Finish-Up-A-Thon Challenge

What I Built

I revived personal_blog, a server-rendered personal blogging platform built with Dart Shelf, PostgreSQL, Mustache templates, and Tailwind CSS.

LandingPage

Repository: https://github.com/AlainDevs/personal_blog

The project started as a bare-bones Dart web app and gradually became a small publishing system with:

  • Public blog pages and individual post pages
  • User registration and login
  • Admin-only pages for posts, categories, users, and settings
  • PostgreSQL persistence
  • Seeded demo content and demo users
  • Tailwind-powered styling
  • Docker Compose setup for one-command local deployment
  • Automated tests for authentication, settings, comments, middleware, and blog routing
  • A separate Docker-based performance testing stack using wrk and Lua

What makes this project meaningful to me is that it is intentionally simple. It is not trying to be another huge CMS. It is a calm, personal publishing space: fast to run, easy to understand, and small enough for one developer to confidently maintain.

Before the comeback, it still felt like a rough prototype. It had pieces of a blog, but it was not easy enough to run, test, benchmark, or explain to another person. The Finish-Up-A-Thon gave me a reason to turn it into something I could actually hand to someone and say: clone it, run one command, log in, and start exploring.

Demo

Project repository: https://github.com/AlainDevs/personal_blog

Run the demo locally with Docker Compose:

git clone https://github.com/AlainDevs/personal_blog.git
cd personal_blog
docker compose up --build
Enter fullscreen mode Exit fullscreen mode

Then open:

http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

Admin area:

http://localhost:8080/admin
Enter fullscreen mode Exit fullscreen mode

Performance report included in the repository:

Performance

The latest recorded smoke benchmark reported:

  • 1,877 total requests in 2.01 seconds
  • 935.31 requests per second
  • 4.27ms average latency
  • 7.45ms 99th percentile latency
  • No reported socket errors
  • No reported non-2xx/3xx responses

Validation checks recorded in the project:

  • docker compose -f docker-compose.performance.yml config --quiet passed
  • dart analyze passed
  • dart test --timeout=30s passed

The Comeback Story

The project had eight commits in total. The first four commits created the original prototype: project structure, Tailwind setup, CRUD-oriented services, Docker scripts, and an early server implementation.

The Finish-Up-A-Thon comeback happened in the top four commits. These were the commits where I used GitHub Copilot, together with my AI coding context and rules including ByteRover, DCM Flutter Guidelines, and AI rules for Flutter/Dart-style development, to push the project from “works on my machine prototype” toward “finished, runnable, documented project.”

GitHub Copilot skills

GitHub Copilot DCM skills

Commit 1: 8345dbd — Docker infrastructure and service layer enhancements

Commit message: Add Docker infrastructure and service layer enhancements

This was the biggest comeback commit. It changed 44 files with 4,183 insertions and 1,577 deletions.

Before this commit, the project had useful pieces, but the architecture was still too tangled. Server setup, routing, database access, auth behavior, and page rendering were not cleanly separated enough for confident testing or future changes.

What changed:

  • Added a cleaner createAppHandler() function so the Shelf app can be built both by the executable and by tests.
  • Improved the middleware flow so JWT auth context is attached to requests.
  • Protected admin pages and admin APIs more consistently.
  • Added DatabaseConnection as a shared PostgreSQL bootstrap layer.
  • Added schema creation and seed data for users, categories, posts, comments, post categories, and application settings.
  • Added an AppSetting model.
  • Added SettingsService and SettingsHandler.
  • Added admin settings UI for toggling public registration.
  • Improved service-layer boundaries for users, posts, categories, comments, and settings.
  • Used safer named SQL patterns through Sql.named style interactions.
  • Updated Docker Compose to use PostgreSQL 18 and a persistent database volume.
  • Added tests for auth, registration settings, comments, and admin middleware.

This commit was where the project stopped being a pile of working code and started feeling like an application with structure.

Copilot helped here by accelerating the repetitive but important parts: service methods, handler wiring, model mapping, test doubles, and route refactors. The AI rules helped keep the generated code closer to consistent Dart conventions instead of random snippets.

admin dashboard

Commit 2: d8832a9 — Safer route parameter handling

Commit message: refactor: Replace direct access to request parameters with a utility function for safer path string retrieval

This was a smaller but important hardening commit.

Before this commit, route handlers accessed path parameters directly, for example by reading request.params['slug'] inline. That works, but it spreads low-level request handling across the codebase.

What changed:

  • Added readPathString(Request request, String key) in request_utils.dart.
  • Updated integer path parsing so readPathInt() builds on top of readPathString().
  • Updated the blog detail route to read the slug through the utility function.
  • Added a test proving that /blog/a-tiny-publishing-checklist renders the correct blog detail page.
  • Added fake post/comment services so the page route can be tested without a real database.

This commit represents the “finish-up” mindset: not just adding features, but reducing fragile patterns and locking behavior with tests.

Copilot helped by suggesting the test structure and the fake service overrides. That let me quickly validate the route behavior rather than only eyeballing the refactor by using MCP.

MCP

Commit 3: f52a043 — Performance testing infrastructure

Commit message: feat: Add performance testing infrastructure with Docker and Lua scripts

This commit added 1,015 lines across seven files.

Before this commit, I could run the app, but I did not have a repeatable way to answer a basic question: “How does it behave under load?”

test result

What changed:

  • Added docker-compose.performance.yml, a separate performance testing stack.
  • Added an Alpine-based performance Dockerfile that builds wrk.
  • Added request_mix.lua for weighted traffic across realistic routes:
    • homepage
    • seeded blog detail pages
    • generated CSS
    • public JavaScript
  • Added generate_report.js, which runs Docker Compose, captures benchmark output, parses results, and writes a GitHub-ready Markdown report.
  • Added PERFORMANCE_RESULTS.md with the latest benchmark output.
  • Added npm scripts:
    • npm run performance:report
    • npm run performance:report:smoke
  • Documented how to tune benchmark load with environment variables.

This was a big step toward making the project feel complete. A personal blog should not only have features; it should be easy to verify that pages respond quickly and that changes do not obviously break performance.

Copilot was especially useful here because the work crossed several small domains: Docker Compose, shell readiness checks, Lua route selection for wrk, Node.js process management, Markdown report generation, and benchmark parsing.

agent in Copilot

Commit 4: fdb4e92 — README polish and beginner-friendly setup

Commit message: docs: Update README to enhance Docker Compose instructions and clarify setup process

This final comeback commit focused on usability.

Before this commit, the README still described the project like a basic Dart web app and told users to install WebDev manually. That no longer matched the revived project by creating our own agent - doc-reviewer.

What changed:

  • Rewrote the README around Docker Compose as the primary way to run the project.
  • Explained that users do not need to install Dart, Node.js, PostgreSQL, or WebDev locally.
  • Added step-by-step startup instructions.
  • Added the local URL: http://localhost:8080.
  • Added seeded admin and reader accounts.
  • Added the admin URL: http://localhost:8080/admin.
  • Added stop, restart, and database reset instructions.
  • Kept the performance testing section so users can generate benchmark reports.

This was the last mile of finishing the project. The app may be technically complete, but if another developer cannot run it easily, it still feels unfinished. This README update made the project approachable.

Copilot helped turn rough notes into a clearer onboarding path and made the documentation more user-focused.

My Experience with GitHub Copilot

GitHub Copilot helped me finish the project in the way I actually needed: not by replacing my decisions, but by keeping momentum while I worked through lots of small, connected tasks.

I used Copilot with my AI development context, including ByteRover, DCM Flutter Guidelines, and AI rules for Flutter/Dart-style development. Even though this is a Dart Shelf web server rather than a Flutter UI app, those rules still helped encourage cleaner structure, explicit tests, safer utilities, and more maintainable code.

The most useful parts of Copilot were:

  • Turning architectural intent into concrete Dart service and handler code.
  • Helping refactor the Shelf server into a testable createAppHandler() structure.
  • Suggesting test cases and fake services for auth, settings, comments, and blog routes.
  • Speeding up repetitive model and mapping work.
  • Helping write Docker Compose and benchmark infrastructure without constantly switching mental context.
  • Helping polish the README so the final project is easier for another person to run.

post list

The before-and-after arc is clear to me:

Before, personal_blog was a promising but unfinished side project. It had the shape of a blog, but it still required too much local setup knowledge, had less confidence around tests, and did not have a clear performance story.

After, it is a Dockerized Dart personal blog with seeded content, admin flows, persistent PostgreSQL storage, application settings, automated tests, performance benchmarking, and beginner-friendly documentation.

That is exactly what I wanted from this challenge: not to start something new, but to finally finish something I already cared about.

post card

Top comments (0)