Shortify - A URL Shortener and QR Code Generator
<!--
Work info
-->
Company:
Independent Project
Role:
Full-Stack Software Engineer
Year:
2025

Project Overview
Shortify is a personal, full-stack project focused on designing and shipping a simple but production-minded URL shortener with QR code generation. The project was intentionally scoped to balance usability, correctness, and deployability without over-engineering.
The goal was not to build the most feature-rich link management platform, but to design a small system end-to-end: backend API design, persistence, frontend UI, and cloud deployment. I owned the entire lifecycle from initial architecture decisions to refactoring, redesign, and deployment.
Background & Motivation
I wanted a project that exercised real system boundaries rather than isolated coding exercises. URL shorteners are deceptively simple: while the core idea is straightforward, the surrounding concerns—persistence, collisions, deployment, UI clarity, and future scalability—introduce meaningful design decisions.
Rather than optimizing prematurely, I approached Shortify as a deliberately staged system: start with simple, correct foundations and leave room for growth.
Problem Statement
The core problem was to design a small but complete system that:
reliably maps short codes to long URLs
supports QR code generation as a first-class feature
persists data across restarts and deployments
presents a clean, usable interface
can be deployed and iterated on like a real product
At the same time, the project needed to avoid unnecessary complexity that would obscure learning or slow iteration.
Approach & Constraints
Several constraints guided the implementation:
The backend needed to be simple, readable, and easy to extend
Database persistence was required, but early development favored speed over scale
The frontend needed a full redesign once initial assumptions proved limiting
Deployment needed to be real, not local-only
The system should have a clear path from “toy” to “production-ready”
Given these constraints, I prioritized clarity and separation of concerns over premature optimization.
Architecture & Technology Stack
Backend Architecture
The backend was implemented using Python and Flask, chosen for its clarity and minimal abstraction overhead. The API was responsible for:
generating unique short identifiers (including support for custom short codes)
persisting mappings between short codes and destination URLs
handling redirect requests and incrementing per-link click counts
generating QR codes associated with each shortened link
logging requests for basic analytics
Persistence was handled using SQLite during initial development to enable fast iteration and zero-infrastructure setup. The schema and access patterns were intentionally designed to allow a future migration to PostgreSQL without rewriting core logic.
The backend was deployed on Render, providing a simple but real production environment for API hosting and persistence.
Frontend Architecture
The frontend was built as a single-page application using React and Tailwind CSS. It was responsible for:
collecting user input for link creation
displaying generated short links and QR codes
presenting basic per-link analytics (e.g., click counts)
providing a responsive experience across desktop and mobile
After an initial implementation, the frontend was deleted and redesigned from scratch. The second iteration focused on clearer user flow, reduced visual noise, and more obvious primary actions, reinforcing the idea that iteration sometimes requires discarding working but suboptimal designs.
The frontend was deployed independently using AWS Amplify, allowing UI changes to be made and deployed without coupling them to backend changes.
System Boundaries & Data Flow
At a high level:
the frontend communicates with the backend via a small, explicit API surface
the backend owns all persistence, redirect handling, and analytics
deployment is intentionally split to mirror real-world service boundaries
This architecture kept responsibilities clear and made the system easier to reason about, debug, and extend.
Key Decisions
Start with SQLite, plan for PostgreSQL
Shortify initially used SQLite to enable fast iteration and minimal setup. Rather than treating this as a dead end, I designed the data access layer with a clear migration path to PostgreSQL in mind.
This allowed me to:
validate schema and access patterns early
avoid infrastructure overhead during early development
plan for production persistence without rewriting core logic
The eventual PostgreSQL migration was treated as an architectural step, not an afterthought.
Treat QR codes as a core feature, not an add-on
QR code generation was designed as a first-class capability. This influenced:
API shape
data models
frontend affordances
By designing QR support early, the system avoided awkward retrofits later.
Redesign the frontend from scratch
After building an initial frontend, I intentionally deleted it and started over. The first version functioned, but it didn’t communicate intent clearly and didn’t scale well as features were added.
The redesign focused on:
simplifying user flow
making primary actions obvious
reducing visual and cognitive clutter
This reset improved the overall coherence of the product and reinforced the value of iteration over attachment.
Separate deployment concerns by responsibility
I split deployment intentionally:
Backend deployed on Render
Frontend deployed on AWS Amplify
This separation mirrored real-world deployment patterns and allowed each side of the system to evolve independently.
Tradeoffs & Risks
Several tradeoffs were accepted intentionally:
SQLite limits scalability but improves iteration speed
A small feature set limits scope but improves quality
Manual deployment configuration trades automation for understanding
These choices were appropriate for the project’s goals and left room for future expansion.
Results & Impact
Shortify resulted in a fully deployed, end-to-end system that behaved like a real product rather than a local demo.
Over the course of development and live usage:
The platform was used to create 150+ short links, including both generated and custom short codes
The redirect endpoint processed 1,000+ redirect requests, with per-link click counts tracked and persisted
QR code generation was integrated directly into the core flow, allowing each short link to be shared visually as well as via URL
A responsive, single-page interface enabled link creation and analytics viewing across desktop and mobile devices
The backend and frontend were deployed independently, validating the system’s ability to evolve without redeployment coupling
Beyond raw usage numbers, the project demonstrated the ability to:
design a complete system across frontend, backend, persistence, and deployment
make architectural decisions that supported iteration rather than locking in early assumptions
treat deployment and observability as part of development instead of as an afterthought
Most importantly, Shortify validated my ability to design, ship, and iterate on a production-minded system independently, from first commit to live usage.
Reflections & Takeaways
Shortify reinforced several principles:
small systems still deserve good architecture
iteration often requires throwing work away
deployment is part of development
designing for migration early reduces future friction
What I Intentionally Did Not Do
I avoided adding features like analytics dashboards, authentication, or link expiration. While useful, they would have obscured the project’s primary learning goals and introduced unnecessary complexity.
Keeping the scope tight made the system easier to reason about and improve.





Results
Redirect endpoints processed
Short links generated
Independently deployed services




