Case study 02 · Web platform
Enalysis
From co-op intern to full-stack developer on a long-running analytics SaaS: framework upgrades, core workflow features and a CSS refactor with proof of zero visual change.
- Role
- Full-stack developer
- When
- 2018–2019, 2022–2026
- Where
- Detechtion.AI
- Platforms
- Web · React · .NET
- failing tests burned down during a staged three-framework upgradeTwo unreliable tests were marked as skipped, visible in every run, rather than deleted.
- 110 → 0
- lines of global CSS deleted with 0 visual differences
- −1,602
- elements diffed across 114 test pages in both themes
- 6,115
- colour literals collapsed into palette tokens, with 0 contrast failures
- 1,886 → ~50
Overview
A long-running SaaS that operators use to monitor and optimize fleets of natural-gas compressors: per-asset performance KPIs, trends and reports, fleet dashboards, field data entry, downtime and maintenance tracking, emissions reporting and embedded analytics. A modern React single-page app and REST API run alongside an older .NET web application.
I first worked on it as a co-op intern (2018–2019), then from 2022 as a developer: modernizing the front end and the API, shipping workflow features and most recently directing an AI-assisted design-system migration where every CSS change had to prove it changed nothing.
My part. Primary author of the 2022 SPA framework upgrade, the API's move to .NET 8, time-zone-correct trend data, the embedded-analytics UI, the downtime event editor and CO₂e emissions metrics. Co-developer of the action-item tracker and an external REST API for asset configuration data.
Try it
Style Regression Lab
A small synthetic dashboard styled by a deliberately messy stylesheet. Snapshot its computed styles, run the refactor steps one by one and watch the diff stay at zero. Then inject a regression and see exactly which element and property it breaks.
- Computed-style snapshot & diff
- Unused-rule detection with a dynamic-class guard
- Cascade-safe rule hoisting
- Live WCAG contrast audit
Engineering stories
Problem, approach, outcome.
01
Deleting 1,600 lines of global CSS, with proof nothing changed
- Computed-style regression testing
- CSS cascade & specificity
- Rollback discipline
Problem
After a framework upgrade and a design-system migration, the theme stylesheets still held framework leftovers, unused classes and rules copied verbatim into both light and dark. In a global stylesheet, any deletion can silently break a screen nobody thinks to check.
Approach
- The refactor ran behind a computed-style regression harness: 114 test pages rendered from the real stylesheets in both themes, with a baseline of computed styles for 6,115 elements. The baseline was taken twice to prove it was deterministic.
- A rule counted as unused only if its class appeared nowhere in the source, including class names built at runtime from template strings. That guard saved status-badge styles a naive pass would have deleted.
- A cascade “move guard” hoisted identical light/dark rules into the shared file only when no rule of equal specificity could start winning by source order. It kept 318 rules where they were.
- Every step was re-diffed against the baseline, with the test suite and formatter run each time.
Outcome
The three stylesheets went from 16,466 to 14,864 lines (−10%): 371 unused rules removed and 47 duplicates moved into the shared file, with 0 rendering differences after every step and all 572 tests passing. Earlier passes using the same method caught two regressions before they landed, and both were rolled back.
- 16,466 → 14,864 lines
- 0 differences · 6,115 elements · 2 themes
- 371 unused rules removed · 318 kept by the guard
An AI coding agent wrote the harness and most edits at my direction. I set the acceptance criteria (zero visual diff, exact values), approved each step and own the result.
02
A re-runnable recolour pipeline for a 15K-line dual-theme stylesheet
- Design tokens
- Idempotent migrations
- WCAG contrast
Problem
The app's look lived in about 15,000 lines of hand-maintained CSS across a shared file and two themes, and one legacy colour did double duty for links and buttons. The new design system required exact token values in both themes, with no rounded or in-between shades. Midway through, the target design itself changed.
Approach
- Set the bar: exact token values everywhere and identical semantics across themes.
- Had an AI agent build a scripted pipeline instead of hand-editing. It classifies colours by selector context (splitting the overloaded colour into link, control and focus roles), snaps neutrals and hues to tokens, and converts alpha colours to exact hex, including inside SVG data URIs.
- After two over-broad find-and-replaces broke working rules, every hand fix became an idempotent script. The whole recolour could then be re-run from a clean tree when the target changed.
- Verification: a same-colour text/background scanner, a cross-theme comparator, a WCAG contrast audit and live computed-style probes.
Outcome
The cross-theme comparator caught about 110 declarations an earlier pass had pushed into the wrong hue family. The final state has zero same-colour collisions and zero contrast failures in both themes, and 1,886 colour literals collapsed into about 50 palette tokens.
- 1,886 literals → ~50 tokens
- 0 contrast failures in either theme
- ~110 misclassified declarations caught
An AI coding agent wrote the tooling. I defined the rules, reviewed both themes by eye and flagged the cross-theme inconsistencies myself.
03
Flash-free theme switching, and a white iframe that only appeared in dark mode
- Browser rendering
- Hypothesis-driven debugging
- Re-baselining
Problem
Switching themes briefly flashed the framework's stock colours. Later, an embedded report showed a white canvas, but only in dark mode, and editing the report's theme made no difference.
Approach
- The flash took three rounds. Swapping the stylesheet's href left an unstyled gap. Toggling media attributes still flashed, because the head-management library recreated the link elements.
- The final fix: static theme links ordered to win the cascade, a pre-paint inline script that applies the saved theme and an effect that flips media on the existing elements, so a switch is a synchronous restyle with nothing to fetch.
- For the iframe, the first two plausible causes were real bugs but not the culprit. As hypotheses piled up, I called a revert to the known-good branch to re-baseline.
- The root cause: a dark colour-scheme set on the page to theme scrollbars. Chromium paints an opaque backdrop behind a cross-origin iframe whose colour scheme differs from its host page's.
Outcome
Theme toggles are instant with no unstyled frame. One scoped colour-scheme declaration fixed the report and kept the themed scrollbars.
I reported the flash and called the revert to re-baseline; an AI coding agent implemented the fixes under my direction.
04
Making a huge multi-select feel instant
- List virtualization
- React memoization
- Keyboard accessibility
Problem
A recipient picker in a create/edit modal lagged badly. It mounted every option as a DOM node and rebuilt its options on every render, so typing anywhere in the modal re-rendered the whole list.
Approach
- A windowed menu list mounts only the visible rows plus a small overscan, with spacers holding the scroll height.
- Keyboard-focused options still scroll into view. Fixed a ping-pong bug where that effect ran before the list had been measured.
- Stable component maps and options memoized on their source list, applied to both modals that share the picker.
Outcome
About 99% fewer mounted option nodes (every option down to ~20), and roughly 10–60 ms of main-thread work per keystroke.
I reported the lag and set the bar; an AI coding agent implemented the virtualization.
05
Upgrading three frameworks in stages, then the API
- Framework migration
- Test-suite recovery
- Incremental risk reduction
Problem
The single-page app was two major versions behind on React, React Router and Bootstrap, and the upgrade broke routing, analytics, maps and much of the test suite.
Approach
- Staged it: Node and packages, then React 18, then React Router 6 (routes, redirects and navigation prompts rewritten), then Bootstrap 5 with its utility-class renames.
- Pulled an analytics package that was incompatible with the new router, then reintroduced analytics through a tag manager.
- Burned down failing tests step by step and marked two unreliable tests as skipped, visible in every run, instead of deleting them.
- Later did the equivalent for the backend: moved the API and shared libraries to .NET 8 and removed their use of deprecated framework APIs.
Outcome
Most dependency-audit findings were cleared, and failing tests fell from 110 to 21, 17, 12, 3 and finally 0. The modernized stack carried the product through the following years of feature work.
- 110 → 0 failing tests
- API moved to .NET 8
06
Time-zone-correct trend data across daylight-saving changes
- Date/time correctness
- C# / .NET
- Edge-case testing
Problem
Trend data came back in local field time, so charts could misplace or duplicate points around daylight-saving transitions, and assets without a configured time zone had no reliable conversion.
Approach
- Resolved each asset's time zone from its settings, falling back to a geographic lookup from its coordinates.
- Converted trend timestamps to UTC in the API, handling local times that don't exist (spring-forward gaps) and times that happen twice (fall-back overlaps).
- Pinned the edge cases with unit tests and refined the logic through review.
Outcome
Trend data comes back in consistent UTC across DST transitions, and the edge cases are covered by tests.
More highlights
- Co-developed a kanban-style action-item tracker: drag-and-drop status changes and ordering, templated notification emails with attachments, comment threads and in-app PDF/photo preview.
- Built most of the embedded analytics area: report embedding, custom light and dark report themes and a mobile layout.
- Added CO₂-equivalent emissions metrics end to end: calculation, simulation, asset vitals and reports.
- Built the downtime event create/edit modal and its save API with dependent dropdowns and validated forms.
- 2018–2019: started the legacy app's Selenium UI suite in CI and built emissions-compliance pages with bulk CSV/XML import and export. Also co-developed an external REST API with request validation and structured error codes.
Stack
- Front end
- ReactReact RouterReact-BootstrapFormik + YupHighchartsEmbedded BI
- Back end
- C# / .NET 8ASP.NET CoreEntity Framework CoreSQL ServerNUnit
- Quality
- Jest + React Testing LibrarySelenium (legacy UI suite)Computed-style diffingHeadless Chrome
What I took away
- “It looks the same” is an opinion. A computed-style diff across every element is evidence.
- Make every fix re-runnable. The day the target changes, you re-run the whole thing instead of redoing it.
- When hypotheses pile up, stop and re-baseline from a known-good state.
Public info
What the company itself has published about this product. It's linked, not copied.