DevSal ingests raw salary reports from public sources, normalizes messy job titles and locations into a canonical schema, runs a statistical engine over each cohort, and serves the results through a REST API and a dark-mode analytics dashboard.
Five stages, from raw CSV rows to percentile-ranked cohorts. The statistical layer is pure functions with no database dependency, so the same code powers ingestion, the API, and ad-hoc analysis.
Stream CSV rows from public sources (Stack Overflow, H1B, levels.fyi, Glassdoor) with a configurable column map. Invalid or non-positive salaries are rejected and counted.
→Regex patterns map ~free-text titles to 16 canonical roles & 8 seniority levels; locations parse into city / region / country; currency converts to USD via an FX table.
→PostgreSQL schema with typed enums, foreign keys to roles / locations / data_sources, a salary_records fact table and a precomputed salary_stats rollup.
Statistical engine: interpolated percentiles, IQR-fence outlier removal, cost-of-living adjustment, confidence × source-reliability weighting, histograms, skewness.
→Express REST API (10 endpoints, Helmet + rate-limit + compression) feeding a React 18 / Recharts dashboard with Overview, Compare, Percentile and Explorer views.
generateDataset(2500), seeded for reproducibility). The distributions are modelled from role × location × seniority × company multipliers with Gaussian noise — realistic in shape, but not collected market data.
Both distributions are right-skewed (positive skew): a dense band of mid-market salaries with a long thin tail of staff-plus and big-tech packages pulling the mean above the median.
The clearest driver in the dataset. Median base compensation grows roughly 7× from intern to fellow, with the steepest jumps at the senior and distinguished thresholds.
Across all seniority levels combined, the spread from the top role to the bottom is about $66k of median base — smaller than a single step up the seniority ladder. AI/ML, security and management lead; QA and design trail.
San Francisco and New York top raw median base salary. But once each city's median is divided by its cost-of-living index (NYC = 100 baseline), the ranking inverts — lower-cost US hubs like Austin, Atlanta and Denver deliver the most real purchasing power, while the Bay Area premium mostly cancels out.
Every record carries a source and a per-source reliability score (0–1). The engine's weighted-mean function multiplies each record's own confidence by its source reliability, so a levels.fyi or H1B data point counts for more than a scraped or survey one.
pg, typed enum schema + migration runnercsv-parse streaming ingest with pluggable column mapsexpress-rate-limit, gzip compressionutils/statistics.js)# backend
cd backend && npm install
node src/migrations/run.js # create schema (needs DATABASE_URL)
npm start # API on :3001 — serves the in-memory
# 2,500-record dataset if no DB is seeded
# frontend
cd frontend && npm install
npm run dev # dashboard on :3000
# ad-hoc ingest
node src/services/ingest.js --file=data.csv --source=levels.fyi
API surface: /api/overview, /api/salaries, /api/salaries/stats, /api/salaries/percentile, /api/salaries/compare, /api/salaries/distribution, /api/roles, /api/locations, /api/sources, /api/health. All read-only, all filterable by role, category, location, experience, company, company size, remote and salary range.