Category Archives: GenAI

Inheriting a Legacy Security Nightmare — And Actually Fixing It

A field note on what it looks like to walk into a five-year-old codebase with no original authors, a fresh security audit, and a deadline.


The Setup

A few months ago, a leader I respect reached out. They had recently inherited ownership of an enterprise web platform — a system that had been quietly doing its job for nearly five years. The original architects, the engineers who built it, and most of the product stakeholders had long since moved on. What remained was a working product, a handful of documentation fragments, and no living memory of why certain decisions were made.

Then the security reports started landing.

Not one or two findings. Dozens. SQL injection. Authentication bypasses. Token issuance endpoints that had no business existing. Some of them had been open for over a year.

They asked if I could help. I said yes. This is what I learned.


The Codebase

The system is a multi-repo monorepo — several Angular front-end apps, a Node/Express server layer for each, and a shared microservices backend. Each layer was built at a different time by a different team. Some modules are polished. Some have the unmistakable fingerprints of “I needed to ship this by Friday.”

The repos had diverged. Region-specific variants had been forked from the main app and then evolved independently. One module had a routing file with a comment block from 2021 that referenced a wiki page that no longer existed.

This is normal. Five years is a long time in software.


Finding 1: The Authentication Surface

The first class of issues was in the front-end layer — two apps that together form the entry point for the platform.

Both apps had a POST handler that would accept a user identifier from the request body and mint a signed JWT in response. No session validation. No verification that the caller was actually logged in. Just: “you sent a user ID, here is a token.”

This is the kind of code that makes complete sense in the context it was written. Someone needed to bridge an external authentication system into a Node app. They had a form POST coming in, they trusted it was legitimate, and they wrote code that acted on that trust. At the time, behind a corporate VPN, it probably felt fine.

Five years later, with a formal third-party security audit in play, it was a critical finding.

The fix wasn’t obvious. The naive answer — “just validate the session server-side” — required the session cookie to be present on the request. But cookies are scoped by path, and the app wasn’t running under the right path for the session cookie to be sent. The fix required a coordinated change: relocate the first app to a new path that fell inside the cookie’s scope, redirect legacy traffic at the routing layer, and rebuild the authentication primitive from scratch using a database session lookup.

Then, for the second app: since it lives at a different path and can never receive the session cookie, it needed a different approach entirely. The answer was to stop minting tokens there completely. Remove the endpoint. Enforce a gate that only accepts tokens already issued by the first app. One authority, one trust anchor.

Shipping both took about two weeks across three PRs. The hardest part wasn’t the code — it was understanding the trust model well enough to know what the code should be.


Finding 2: SQL Injection at Scale

While the auth work was wrapping up, the second wave of security reports arrived.

Dozens of SQL injection findings across the backend microservices.

The root cause was the same in every single case. A query template with a named placeholder. A service method that substituted user input directly into the query string using string replacement. Then that string, with user data baked in, passed directly to the database driver’s non-parameterized execution path.

// The vulnerable pattern - repeated across the codebase
const query = queries.FETCH_DATA.replace('@param', userInput);
await db.execute(query);

The fix was equally uniform:

// The fixed pattern
await db.executeWithParams(queries.FETCH_DATA, { param: userInput });

The DAO layer already had a parameterized execution method. It was sitting right there, unused for this pattern, presumably because the original author didn’t know it existed or the pattern hadn’t been established yet when these service files were written.

File after file. Same fix. Different parameter names.

This is what happens when a pattern becomes a convention without being reviewed. The first person writes it one way. The next person copies it. Five years later you have dozens of instances of the same mistake, all surfaced in a single audit sweep.


What I Actually Learned

1. Legacy codebases aren’t broken — they’re contextless.

Every strange decision I found had a reason. It just wasn’t written down, or the person who knew it had left. The code isn’t wrong. It’s just been outlived by the threat model it was written against.

2. Understanding the trust model is the whole job.

The hardest part of the auth fix wasn’t writing the validation logic. It was sitting down and drawing out: who mints tokens? who verifies them? what cookies are available where and why? Once that was clear, the code was almost mechanical. Before that clarity, every PR felt risky.

3. Consistent bad patterns are actually a gift.

Dozens of SQL injection findings sounds catastrophic. But because the root cause was identical in every case, fixing them was systematic. One engineer, a clear pattern, and enough time. Inconsistent vulnerabilities — each with a different root cause — would have been far harder.

4. Documentation debt compounds like financial debt.

The hours spent reverse-engineering why the path routing worked the way it did, why certain cookies were set, why a particular environment flag existed — all of that was time not spent fixing things. Every future engineer who touches this system will either pay that cost again or benefit from the ADRs and notes I’m leaving behind.

5. Coming in as an outsider is a superpower.

I had no attachment to the original decisions. I could ask “why does this exist?” without anyone feeling criticized. The leader who brought me in had the same openness. That made hard conversations — “this needs to be deleted, not patched” — much easier than they might have been.


The Tool That Was Actually in the Room

I want to be honest about something that doesn’t get talked about enough in engineering write-ups: I didn’t do this alone, and the co-author wasn’t human.

I used an AI coding assistant — specifically Cursor with Claude — as a hands-on collaborator throughout this entire remediation. Not as an autocomplete tool. Not to generate boilerplate. As an actual working partner that wrote code, reasoned about trust models, drafted architecture decision records, and produced PR-ready diffs.

Here’s what the actual working loop looked like, as honestly as I can describe it:

  1. I’d share the problem — a security finding, a broken behaviour, a constraint I’d discovered in the codebase.
  2. The AI would propose a solution — sometimes code, sometimes a question back at me, sometimes a structured document laying out options.
  3. I’d push back. “This doesn’t work because the cookie isn’t scoped here.” “This will cause an infinite redirect.” “The infrastructure config is wrong — look at where the volume actually mounts.”
  4. The AI would revise. Not defensively. Just: absorb the feedback, update the model, try again.
  5. Repeat until I was confident enough to push.

Looking at the git log across both repos, 13 commits landed over the course of about a week. The path relocation, the session validation gate, the impersonation detection, the JWT entry gate on the second app, the infrastructure fix, the redirect loop fix — all of it went through this loop. The AI wrote most of the initial code. I caught the gaps. Together we got to something I was willing to put in production.

What surprised me wasn’t that the AI could write the code. That part I expected. What surprised me was how useful it was to have something that could hold the full context of the problem — multiple ADRs, a security finding write-up, a Node server file, a routing config — and reason across all of it at once. The kind of reasoning that would normally require a senior engineer who’d been on the codebase for months.

That said: the AI was wrong, regularly. It made assumptions about the environment. It occasionally proposed fixes that would have introduced new problems. It didn’t know things I knew from reading an infrastructure config at 11pm. The loop only worked because I knew enough to catch those gaps.

Reproducing production locally — the nginx proxy trick. Before any browser-driven testing could work, I had to solve a subtler problem: cookie-scoped authentication simply cannot be tested with separate localhost ports. A cookie scoped to a specific path on one port is invisible to an app running on a different port. To faithfully reproduce the production cookie flow, CORS behaviour, and CSRF constraints on my laptop, I stood up a local nginx reverse proxy on a custom hostname with path-based routing — unifying the home app, the reports app, and the microservice backend under a single origin, mirroring production exactly. Only then did the cookie handoff between apps behave the way it would in the real environment.

Testing without writing tests. One thing that genuinely surprised me: I never wrote a single line of Playwright test code. Cursor has an MCP integration that connects the AI agent directly to a live Chrome instance via Chrome DevTools Protocol. Combined with an agent browser tool, I could describe what I wanted to verify — “log in, navigate to the protected page, confirm the JWT gate blocks unauthenticated requests” — and the agent would drive the browser, execute the flow, take screenshots, and report back. End-to-end test coverage for security-critical flows, with zero test-authoring overhead on my end.

Attacking my own fixes. On the offensive side, I used Burp Suite Community Edition to validate the fixes before shipping. Replaying captured requests with tampered payloads, testing the SQL injection patterns against the parameterized endpoints, probing the JWT gate with malformed and expired tokens. If Burp could break it, the fix wasn’t done. If it couldn’t, I had reasonable confidence the patch held. Having both the AI writing the defence and a real attack tool probing it created a tighter feedback loop than code review alone would have.

Which is, I think, the actual lesson: AI assistance in this kind of work isn’t about replacing engineering judgment. It’s about removing the friction between having a judgment and turning it into working, tested, and validated code. That friction — the “I know what needs to happen but now I need to write 80 lines of middleware, a test suite, and then manually click through the app to verify it” friction — is where a lot of security work quietly stalls.

It didn’t stall here.


Where Things Stand

The auth fixes are in production. The SQL injection remediation is in progress. The codebase has more documentation today than it did three months ago. The security findings are being closed.

The original team who built this shipped something that ran reliably for five years and served a large internal user base. That’s genuinely hard to do. What I’m doing now isn’t a criticism of them — it’s just the next chapter.

Legacy systems don’t need heroes. They need patience, curiosity, and someone willing to read a five-year-old routing config until it makes sense.


Written by a developer who spent way too long reading cookie scope documentation and came out the other side with opinions.

How I Built a 123,000 LOC Enterprise Platform in 4.4 Months as a Solo Developer

A deep dive into productivity gains, lessons learned, and the numbers behind building an enterprise analytics platform


The Challenge

In August 2025, I started building an enterprise analytics and governance platform from scratch. The scope was ambitious: natural language SQL queries, semantic search, row-level security, column-level security, attribute-based access control, dashboard builders, and more.

Traditional estimates suggested this would take 5.5 years with a single developer, or require a 12-person team working for several months.

I delivered it in 4.4 months. Solo.

Here’s the breakdown.


By The Numbers

What Was Delivered

MetricValue
Lines of Code123,430
Stories Completed100
Epics14 (13 completed, 1 planned)
Microservices6
Development PeriodAugust 7 – December 21, 2025

The Math: Traditional vs Actual

Traditional Solo Developer Estimate:

Story Points: 1,584 SP (all 14 epics)
Velocity: 12 SP per 2-week sprint (industry average)
Sprints Required: 1,584 ÷ 12 = 132 sprints
Timeline: 132 sprints × 2 weeks = 264 weeks = 66 months = 5.5 years

What Actually Happened:

Actual Effort: 211 SP (complexity-adjusted)
Velocity: 28 SP per 2-week sprint
Sprints Required: 211 ÷ 28 = 7.5 sprints
Timeline: 7.5 sprints × 2 weeks = 15 weeks ≈ 4 months

The Multipliers

  • Base Velocity: 2.3x faster (28 SP vs 12 SP per sprint)
  • Complexity Reduction: 7.5x (1,584 SP → 211 SP actual effort)
  • Overall Timeline: 15x faster than traditional estimates
  • Team Equivalence: Delivered what would traditionally require a 12-person team

What Did I Build?

The platform consists of 6 microservices plus a React frontend:

Frontend:Web UI (75,991 LOC) – React 19 with Dashboard Builder V1 & V2, 30+ widget types. Compiled into static files and served by the Service Layer.

Backend Microservices:

1. Service Layer (17,088 LOC) – Spring Boot 3.2 with OAuth2 authentication
2. Core API (11,600 LOC) – FastAPI with cloud data warehouse gateway and caching
3. NL-to-SQL Engine (7,250 LOC) – Natural language to SQL engine with LLM integration
4. Data Firewall (6,561 LOC) – SQL-level security with RLS, CLS, and ABAC
5. Semantic Search (3,263 LOC) – Semantic search with FAISS vector database
6. AI Integration Layer (1,677 LOC) – MCP protocol server for AI tool integration


Comparison with Traditional Development

AspectTraditionalActualImprovement
Planning2-3 weeks per epic2-3 days per epic7-10x faster
Implementation50-60 weeks16-18 weeks3x faster
Testing8-10 weeks2-3 weeks3-4x faster
Documentation4-6 weeks1 week4-6x faster
Overall Timeline66 months (5.5 years)4.4 months15x faster
Team Size12 developers1 developer92% reduction

Success Factors

1. Architectural Excellence

I spent significant time upfront on architecture. The microservices approach wasn’t just about scalability—it was about cognitive load management.

Key architectural decisions:

  • 6 independent, scalable microservices – Each service could be developed, tested, and deployed independently
  • API-First Design – Clear contracts with OpenAPI documentation enabled parallel development of frontend and backend
  • Clean Separation of Concerns:
    • Data Firewall for ALL security (RLS/CLS/ABAC)
    • Core API for ALL data warehouse interactions
    • Service Layer for ALL authentication
  • Technology Fit – Python for SQL parsing, Java for auth, React for UI – each technology chosen for what it does best

No overlap. No confusion. When debugging, I always knew exactly which module to look at.

2. Development Best Practices

  • Incremental Delivery: 100 stories over 132 days = 0.76 stories per day. No big bang releases—every day something shipped.
  • Git Discipline: 945+ commits over 132 days = 7+ commits per day. Small, focused commits with clear messages.
  • Security First: OAuth2, RBAC, RLS, CLS, ABAC built into the foundation from day one—not bolted on later.
  • Documentation: Complete JIRA stories and technical documentation maintained throughout development.

3. Technology Choices

I chose technologies based on what they were best at, not what was trendy:

TechnologyPurposeWhy This Choice
sqlglotSQL parsingBest SQL parsing library available; Python-only, which drove Data Firewall’s language choice
React Grid LayoutDashboard V2Proven, battle-tested library for drag-and-drop grid layouts
AG GridData tablesEnterprise-grade data grid with sorting, filtering, pagination out of the box
FAISSVector searchFacebook’s library for efficient similarity search; enables local RAG without external APIs
FastMCPAI tool integrationMCP protocol server for connecting AI tools like Cursor and Claude Desktop
Spring SecurityAuthenticationBattle-tested for enterprise OAuth2/JWT flows

4. AI-Assisted Development

A significant productivity multiplier came from AI coding assistants. I used multiple tools depending on the task:

ToolUse CaseContribution
Claude CodeComplex refactoring, architecture decisions, multi-file changesDeep understanding of codebase context; handled intricate cross-service changes
CursorDay-to-day coding, quick implementationsFast inline completions; excellent for iterating on UI components
GitHub CopilotBoilerplate code, repetitive patternsAccelerated writing of tests, DTOs, and standard CRUD operations
WindsurfCode exploration, understanding unfamiliar codeHelpful for navigating large codebases and understanding dependencies

The key wasn’t replacing thinking with AI—it was offloading the mechanical work. Architecture decisions, security design, and debugging complex issues still required human judgment. But writing boilerplate, generating test cases, and implementing well-defined patterns? AI tools handled those efficiently.

This combination reduced the “typing overhead” and let me focus on the hard problems: SQL injection prevention in the Data Firewall, cascading variable resolution, and JWT token propagation across services.


What Worked Exceptionally Well

  1. Clear Architecture from Start – Well-defined module boundaries enabled focused development and easier debugging. I never had to wonder “where does this code belong?”

  2. Microservices Done Right – Independent modules allowed me to work on one service without breaking others. Each service had its own repository, its own tests, its own deployment.

  3. Incremental Approach – Building features incrementally with regular testing reduced risk dramatically. When something broke, I knew it was in the last day’s work.

  4. Git Discipline – 945+ commits provided a clear development history. Git bisect became invaluable for tracking down issues.

  5. Technology Fit – Choosing the right tool for each job paid dividends:

    • Python + sqlglot for SQL parsing (no equivalent in Java/JavaScript)
    • Java + Spring Security for enterprise auth
    • React + TypeScript for type-safe UI development
  6. API-First Design – Defining API contracts early meant frontend and backend could be developed in parallel. No waiting for the other side to be “ready.”


Challenges Overcome

Not everything was smooth sailing. Here’s what was genuinely hard:

SQL Parsing Complexity

Building a Data Firewall that could inject WHERE clauses into arbitrary SQL while handling nested queries, CTEs, and JOINs was the hardest technical challenge. sqlglot’s learning curve was steep, but once mastered, it was incredibly powerful.

Cascading Variables

Dashboard variables that depend on other variables require topological sorting to resolve in the correct order. A user selects “Region” → that filters “Market” → that filters “Store”. Getting the dependency resolution right took multiple iterations.

Per-Instance Widget Caching

The dashboard builder allows multiple instances of the same widget with different configurations. Architecting efficient per-instance caching with UUID tracking while maintaining cache coherence was tricky.

JWT Token Flow

Propagating user context from the frontend → Spring Boot → Data Firewall → Core API—while maintaining security at each hop—required careful architecture. Each service needed to validate and forward the JWT correctly.

Full-Stack Coordination

Keeping 6 microservices plus a React frontend in sync across Spring Boot and multiple Python services was a constant balancing act. A breaking change in one service could cascade.

Multi-Environment Management

Managing dev/staging/prod configurations across all modules, with different OAuth providers, database credentials, and data warehouse projects, required disciplined configuration management.

Security Compliance

Meeting security scanning requirements meant addressing vulnerabilities as they were found, not deferring them. This added overhead but resulted in a more secure codebase.


Future Improvements

No project is ever truly “done.” Here’s what I’d tackle next:

  1. Automated E2E Testing – Expand test coverage with Selenium or Playwright. I relied too heavily on manual testing.

  2. Performance Monitoring – Implement Prometheus/Grafana observability. Currently, debugging performance issues requires digging through logs.

  3. Distributed Caching – Each service has its own cache. A shared Redis layer would improve consistency and reduce duplicate data.

  4. Dashboard V2 Enhancements – Additional widget types, more templates, and improved drag-and-drop UX.

  5. Advanced Analytics – Epic 11 (the one planned epic) covers scheduled reports, data exports, and executive dashboards.


The Takeaway

The numbers are real. 123,430 lines of code. 100 stories. 4.4 months. Solo.

But the numbers don’t tell the whole story. What made this possible wasn’t superhuman coding speed—it was:

  1. Clear architecture that reduced cognitive load
  2. Right technology choices for each problem
  3. Disciplined incremental delivery
  4. Security built in from day one
  5. Relentless focus on what mattered

The productivity multipliers compound. A 2.3x velocity improvement combined with 7.5x complexity reduction doesn’t give you 9.8x—it gives you 15x+ because the benefits reinforce each other.

Could I do it again? On a different project, with different constraints? Maybe. The principles would transfer. The specific numbers might not.

But one thing I know for sure: the traditional estimates of 5.5 years or 12-person teams aren’t wrong—they’re based on how software ‘was’ typically built.

The Complete AI App Guide for 2025: Essential Tools for IT Professionals

The Complete AI App Guide for 2025: Essential Tools for IT Professionals

Artificial Intelligence is reshaping every layer of software development, from coding and testing to documentation and deployment. This guide curates the most impactful AI applications of 2025, with a special focus on tools that elevate an IT professional’s daily workflow.

Software Development & Programming

Cursor

AI-first code editor that understands your entire project context, offers natural-language refactors, and ships with multi-model support.

GitHub Copilot

Your AI pair programmer for instant code completions, chat-based explanations, and automated tests.

Replit AI Agent

Describe an idea in plain English and watch Replit spin up a working web or mobile app, complete with hosting.

Codeium

Fast, free autocomplete that plugs into 70+ languages and every major IDE.

Anychat

Unified chat interface where you can swap between multiple AI models mid-conversation.

Claude Code

Terminal-native assistant for deep codebase understanding and cross-file edits.

General AI Assistants

  • Perplexity – instant, cited answers for technical research.
  • Claude – long-context reasoning and collaborative project chat.
  • ChatGPT – versatile chatbot with voice mode and strong coding skills.

Productivity & Workflow

  • Granola – turns meetings into structured notes, action items, and summaries.
  • Wispr Flow – system-wide voice dictation that works in any app.
  • Gamma – generate slide decks, documents, or one-page sites from prompts.
  • Adobe AI Assistant – chat with long PDFs, contracts, or manuals.
  • Cubby – collaborative research workspace with built-in AI search.
  • Cora – inbox triage and auto-reply generation for email power users.
  • Lindy – no-code builder for custom AI agents that automate routine tasks.
  • Notion AI – smarter docs, wikis, and databases with in-line generation.

Content Creation & Video

  • HeyGen – realistic AI avatars for tutorials, product demos, and localization.
  • Delphi – voice, video, and text clones for audience engagement.
  • Argil – quick social-media videos featuring AI hosts.
  • Opus – auto-splits long videos into shareable viral clips.
  • Persona – build AI agents that reflect your personal brand.
  • Captions – automatic subtitles, eye-contact correction, and AI presenters.

Creative Tools

  • ElevenLabs – ultra-realistic, multilingual text-to-speech voices.
  • Suno & Udio – compose full songs from a written prompt.
  • Midjourney, Ideogram, Playground – high-quality image generation suites.
  • Runway, Kling, Viggle – next-gen video generation platforms.
  • Krea – canvas for mixing and remixing AI images or clips.
  • Photoroom – one-click product shots, background removal, and batch edits.

Learning & Personal Development

  • Rosebud – interactive journaling with data-backed insights.
  • Good Inside – parenting advice with personalized AI support.
  • Ada Health – symptom assessment and health guidance.
  • Ash – AI-based mental-health coach.
  • NotebookLM – convert any document into an AI-driven podcast.
  • Particle – bite-sized news summaries with source links.

Entertainment & Fun

  • Remix – social platform for sharing AI-generated art and video.
  • Meta Imagine – create playful AI avatars inside Meta apps.
  • Grok – chat companion with a sense of humor from xAI.
  • Curio – interactive toys powered by AI voices.

Getting Started: A Six-Week Integration Roadmap

  1. Weeks 1–2 – Lay the Foundation: Install Cursor or GitHub Copilot in your IDE, plus Codeium as a free backup. Set up ChatGPT and Claude for on-demand problem solving.
  2. Weeks 3–4 – Boost Productivity: Adopt Notion AI for project docs, leverage Adobe AI Assistant for reading specs, and generate slides with Gamma.
  3. Weeks 5–6 – Automate & Scale: Build Lindy agents for repetitive tasks, prototype ideas in Replit, and add professional narration with ElevenLabs.
  4. Beyond: Join AI developer communities, stay updated on new releases, and gradually expand your toolkit.

The future belongs to developers who treat AI as a creative partner, not a replacement. Choose one or two of these tools today, master them, and watch your productivity soar.