<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Giri Manohar Vemula</title>
    <description>The latest articles on DEV Community by Giri Manohar Vemula (@manoharvit).</description>
    <link>https://dev.to/manoharvit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4159529%2F3f48da23-5414-4098-bdaa-11b799e46003.png</url>
      <title>DEV Community: Giri Manohar Vemula</title>
      <link>https://dev.to/manoharvit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manoharvit"/>
    <language>en</language>
    <item>
      <title>From Messy Scripts to Production Minded Architecture: Refactoring Job Copilot</title>
      <dc:creator>Giri Manohar Vemula</dc:creator>
      <pubDate>Sat, 03 Oct 2026 10:37:38 +0000</pubDate>
      <link>https://dev.to/manoharvit/from-messy-scripts-to-production-minded-architecture-refactoring-job-copilot-16md</link>
      <guid>https://dev.to/manoharvit/from-messy-scripts-to-production-minded-architecture-refactoring-job-copilot-16md</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Building a portfolio project often starts with a single script and a dream. That was exactly how &lt;strong&gt;Job Copilot&lt;/strong&gt; began. I needed a tool to help me navigate the chaotic job search process, optimize my resume for different roles, and track where I had applied. &lt;/p&gt;

&lt;p&gt;Job Copilot is an AI-powered career platform designed to make job hunting less painful. It features a dashboard for application tracking, profile management, and experimental AI tools powered by the Google Gemini API for resume tailoring and interview prep. It was a fun project that solved a personal need, and I wanted to use it to showcase my backend engineering skills to potential employers.&lt;/p&gt;

&lt;p&gt;But there was a problem. Once the initial excitement faded and the features were "working," I took a hard look at the codebase. It was an unmaintainable mess. I realized that if a hiring manager looked at this repository, it wouldn't demonstrate production readiness. It demonstrated how to hack a prototype together over a weekend. That realization was the turning point. I decided to pause feature development and refactor Job Copilot into a platform with a clean, professional architecture.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This isn't a tutorial. It's a case study in refactoring decisions, trade-offs, and lessons learned. Code snippets are illustrative, not copy-paste ready.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problems I Found
&lt;/h2&gt;

&lt;p&gt;When I audited my own codebase, the reality was sobering. Here are the specific issues that were preventing this project from being production-ready:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero Automated Tests:&lt;/strong&gt; I was relying entirely on manual clicking to verify features. I found critical bugs simply by navigating the UI in unexpected ways. Without tests, any new feature risked breaking existing functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardcoded Secrets in &lt;code&gt;.env.example&lt;/code&gt;:&lt;/strong&gt; In my rush to build, I had accidentally committed a live Gemini API key into the example environment file. This is a classic security blunder that exposes credentials to the public internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mixed Concerns:&lt;/strong&gt; My FastAPI routes were a tangled web. The endpoint functions were parsing requests, querying the SQLite database directly, and applying business logic all in one place. There was absolutely no separation of concerns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Database Migration System:&lt;/strong&gt; I was using SQLAlchemy's &lt;code&gt;Base.metadata.create_all(bind=engine)&lt;/code&gt; at import time. Every time I altered a model schema, it broke the existing database. My "migration strategy" involved manually dropping tables or writing ad-hoc SQL scripts to patch the schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Vulnerabilities:&lt;/strong&gt; The backend was configured with CORS &lt;code&gt;allow_origins=["*"]&lt;/code&gt;, meaning any malicious website could read my local data. Furthermore, the server was binding to &lt;code&gt;0.0.0.0&lt;/code&gt; instead of &lt;code&gt;127.0.0.1&lt;/code&gt;, exposing it to the entire local network without any authentication mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluttered Repository Root:&lt;/strong&gt; My root directory contained over 40 ad-hoc scripts. Files like &lt;code&gt;patch_db.py&lt;/code&gt;, &lt;code&gt;test_api.py&lt;/code&gt;, and &lt;code&gt;dummy_data.py&lt;/code&gt; were mixed in with the actual application code, making it impossible for a newcomer to figure out where the real application lived.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Refactor
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Repository Structure Cleanup
&lt;/h3&gt;

&lt;p&gt;The first step was aggressively cleaning up the workspace. The root directory was a graveyard of deploy artifacts, legacy scripts, and test outputs.&lt;/p&gt;

&lt;p&gt;I moved the core application into a clean &lt;code&gt;backend/&lt;/code&gt; directory, structured by domain features like &lt;code&gt;app_tracker/&lt;/code&gt;. I created a &lt;code&gt;docs/&lt;/code&gt; folder for architectural documentation and API references. All legacy compatibility scripts were safely archived outside the repository. &lt;/p&gt;

&lt;p&gt;Finally, I extended the &lt;code&gt;.gitignore&lt;/code&gt; to strictly exclude &lt;code&gt;node_modules/&lt;/code&gt;, &lt;code&gt;static_*&lt;/code&gt;, &lt;code&gt;.pytest_cache/&lt;/code&gt;, and &lt;code&gt;.coverage&lt;/code&gt;. The repository instantly became navigable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Layered Architecture
&lt;/h3&gt;

&lt;p&gt;The biggest architectural change was splitting the tangled FastAPI routes into a strict layered architecture. I wanted the codebase to reflect enterprise patterns.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Routes:&lt;/strong&gt; Now strictly handle HTTP requests and responses. They contain zero business logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services:&lt;/strong&gt; This layer owns the business rules, ensures valid state transitions, and handles idempotency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repositories:&lt;/strong&gt; Responsible exclusively for data access and ensuring queries are properly scoped to the owner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Models:&lt;/strong&gt; Pure SQLAlchemy ORM entities representing the database schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how the Application Tracker module is structured today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/app_tracker/
├── domain.py       # State machine, allowed transitions
├── schemas.py      # Pydantic request/response models
├── repository.py   # Owner-scoped queries
├── service.py      # Business rules, idempotency
└── router.py       # FastAPI routes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation ensures data flows in one direction and components remain highly testable. Below is the Mermaid architecture diagram showing how the data flows from the UI down to the SQLite database.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    subgraph API["API layer - FastAPI"]
        V1["/api/v1/applications"]
    end

    subgraph Services["Service Layer"]
        SVC["ApplicationService"]
        DOMAIN["Domain Rules"]
    end

    subgraph Data["Data Layer"]
        REPO["ApplicationRepository"]
        ORM["SQLAlchemy models"]
        DB[("SQLite/PostgreSQL")]
    end

    V1 --&amp;gt; SVC
    SVC --&amp;gt; DOMAIN
    SVC --&amp;gt; REPO
    REPO --&amp;gt; ORM
    ORM --&amp;gt; DB&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;
  
  
  3. Testing Strategy
&lt;/h3&gt;

&lt;p&gt;I went from zero tests to a robust suite of &lt;strong&gt;156 automated tests&lt;/strong&gt;, covering both unit and integration boundaries. &lt;/p&gt;

&lt;p&gt;By mocking external dependencies, I ensured tests run blazingly fast in isolated, temporary SQLite instances. They never touch the real database. We also added legacy compatibility tests to ensure that refactoring didn't break backward compatibility for older clients.&lt;/p&gt;

&lt;p&gt;Here is an example of how we test state transitions in the Application Tracker to ensure valid domain logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_valid_status_transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Setup
&lt;/span&gt;    &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApplicationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create application
&lt;/span&gt;    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tech Innovators&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Backend Engineer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Applied&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Transition status
&lt;/span&gt;    &lt;span class="n"&gt;updated_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;app_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;new_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Interviewing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Assert successful transition
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;updated_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Interviewing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updated_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Database Migrations
&lt;/h3&gt;

&lt;p&gt;Relying on &lt;code&gt;create_all()&lt;/code&gt; was a recipe for disaster. To fix this, I integrated &lt;strong&gt;Alembic&lt;/strong&gt; for proper database schema versioning.&lt;/p&gt;

&lt;p&gt;Now, every schema change generates a migration file with clear upgrade and downgrade paths. For example, Migration &lt;code&gt;0002&lt;/code&gt; safely added a &lt;code&gt;users&lt;/code&gt; table, an &lt;code&gt;owner_id&lt;/code&gt; foreign key, and a new &lt;code&gt;application_status_history&lt;/code&gt; table without dropping existing user data.&lt;/p&gt;

&lt;p&gt;To prevent accidental corruption, I implemented a strict &lt;strong&gt;Schema Guard&lt;/strong&gt;. If a user tries to start the server with an outdated schema, the guard intercepts the startup, refuses to run, and prompts the user to explicitly run the migration command.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Security Fixes
&lt;/h3&gt;

&lt;p&gt;Security in a local-first application is often overlooked, but it is critical. My first action was revoking and rotating the accidentally exposed Gemini API key. &lt;/p&gt;

&lt;p&gt;Next, I addressed the network vulnerabilities. I replaced the dangerous CORS wildcard (&lt;code&gt;*&lt;/code&gt;) with an explicit allowlist restricted strictly to the frontend dev server and the browser extension ID. I also changed the &lt;code&gt;uvicorn&lt;/code&gt; binding from &lt;code&gt;0.0.0.0&lt;/code&gt; to &lt;code&gt;127.0.0.1&lt;/code&gt; so the server is only accessible from the local machine.&lt;/p&gt;

&lt;p&gt;Finally, I scrubbed all hardcoded Personally Identifiable Information (PII) from the &lt;code&gt;DEFAULT_PROFILE&lt;/code&gt; constants, replacing them with empty placeholders, and updated the &lt;code&gt;.env.example&lt;/code&gt; to only show dummy values.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Dependency Management
&lt;/h3&gt;

&lt;p&gt;A project is only as stable as its dependencies. The original &lt;code&gt;requirements.txt&lt;/code&gt; was just a loose list of package names. &lt;/p&gt;

&lt;p&gt;I added missing dependencies that were previously installed globally (&lt;code&gt;python-docx&lt;/code&gt;, &lt;code&gt;fpdf2&lt;/code&gt;, &lt;code&gt;alembic&lt;/code&gt;, &lt;code&gt;pydantic-settings&lt;/code&gt;). More importantly, I established a strict dependency management policy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Upper Bounds:&lt;/strong&gt; Every top-level dependency in &lt;code&gt;requirements.in&lt;/code&gt; is now pinned with a strict upper version bound to prevent unexpected breaking changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hash-Locked Lockfiles:&lt;/strong&gt; I used &lt;code&gt;uv pip compile --generate-hashes&lt;/code&gt; to create a fully reproducible, hash-verified &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI Enforcement:&lt;/strong&gt; The GitHub Actions CI pipeline now installs packages using the &lt;code&gt;--require-hashes&lt;/code&gt; flag. This guarantees protection against supply-chain attacks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is what the locked requirements process looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# requirements.in&lt;/span&gt;
fastapi&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;0.142.2,&amp;lt;0.143
uvicorn&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;0.54.0,&amp;lt;0.55
sqlalchemy&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;2.1.1,&amp;lt;2.2

&lt;span class="c"&gt;# Generate secure lockfile&lt;/span&gt;
uv pip compile &lt;span class="nt"&gt;--generate-hashes&lt;/span&gt; &lt;span class="nt"&gt;--allow-unsafe&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; requirements.txt requirements.in
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Honest moment:&lt;/strong&gt; I almost gave up halfway through the refactor. The legacy code was so tangled that fixing one thing broke three others. Tests made it possible to proceed with confidence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Refactoring Job Copilot was a massive learning experience. Here are the core takeaways that will influence every project I build from now on.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;1. Tests Are a Design Tool, Not Just Verification&lt;/strong&gt;&lt;br&gt;
Writing tests forced me to think critically about my interfaces and dependencies. When a route was hard to test, it usually meant the route was doing too much. The 156 tests I wrote made the massive refactor safe, immediately catching regressions. I am now fully converted to a TDD-ish approach: writing the test interface before implementing the feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Hash-Locked Dependencies Prevent "Works on My Machine" Issues&lt;/strong&gt;&lt;br&gt;
The hash-locked &lt;code&gt;requirements.txt&lt;/code&gt; ensures reproducible builds across any machine. During the refactor, &lt;code&gt;ruff&lt;/code&gt; updated its ruleset, which caused my local environment to format code differently than the CI server. The lockfile caught this discrepancy immediately, proving its worth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Honest Documentation Builds Trust&lt;/strong&gt;&lt;br&gt;
It is tempting to make a portfolio project sound like an enterprise SaaS platform. Instead, I made the README explicitly honest: "local-only, single-user, no authentication yet." I added a &lt;code&gt;SECURITY.md&lt;/code&gt; file admitting current limitations. Transparency over marketing speak builds immediate credibility with other engineers reviewing the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Data Safety Over Convenience&lt;/strong&gt;&lt;br&gt;
The schema guard completely disabled the "convenience" of auto-migrating databases on startup. Instead, it forces an explicit backup and migrate step. It is always better to fail safely and loudly than to quietly corrupt user data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Job Copilot is in a much better place, but software is never truly finished. Here is the roadmap for the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short-term (this month)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User Authentication:&lt;/strong&gt; Implementing robust JWT access and refresh tokens to move away from the single-user local model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Database:&lt;/strong&gt; Integrating PostgreSQL via Docker Compose for production deployments, phasing out SQLite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community:&lt;/strong&gt; Creating "good first issues" and improving the contribution guidelines to welcome open-source collaborators.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Long-term (if traction grows)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenancy Support:&lt;/strong&gt; Architecting the database and services to securely handle multiple distinct users on a hosted platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Hosting Option:&lt;/strong&gt; Providing a one-click deployment option to a cloud provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced AI Features:&lt;/strong&gt; Adding unlimited cover letter generation and interactive voice-based interview mock sessions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;If you are a backend developer looking for a clean FastAPI reference architecture, or a job seeker who wants to run their own local AI career assistant, I invite you to try Job Copilot!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick start (Local SQLite):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ManoharVit/job-copilot.git
&lt;span class="nb"&gt;cd &lt;/span&gt;job-copilot
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
./start.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;With Docker (PostgreSQL + zero setup):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run the test suite:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backend pytest backend/tests/ &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's Connect
&lt;/h2&gt;

&lt;p&gt;If you're building something similar or have questions about the refactor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/ManoharVit/job-copilot" rel="noopener noreferrer"&gt;https://github.com/ManoharVit/job-copilot&lt;/a&gt; (open an issue!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/manohar511/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/manohar511/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm always happy to discuss architecture, testing strategies, or career advice.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>opensource</category>
      <category>python</category>
    </item>
  </channel>
</rss>
