Inspiration

We were inspired by a problem experienced at the school of one of our team members. This year, in her senior year of high school, a new policy was passed that all NYC schools have to decrease class sizes, capped at 25 for high school, 23 for middle school, and 20 for elementary, by 2030. For her school, Brooklyn Tech, this was a big problem: classes are currently averaging 35 students, and lowering to 25 would leave the building without enough physical space. On top of that, the school would need to hire dozens of new teachers just to split sections into smaller groups. We realized this isn't just Brooklyn Tech's problem, it affects thousands of students across NYC. School administrators face a genuine forcing function: they have very few years to find a solution and most don't know where to start. We built ClassFit to help them understand the severity of the crisis at their school and give them concrete, ranked steps they can take: which rooms can be split, which nearby schools have capacity, whether new construction is actually coming. The hope is that this project smooths an extremely timely transition that will shape the future of New York's schools.

What it does

ClassFit NYC maps every NYC school and highlights those with class size compliance gaps. For each school, it provides a toolkit of immediate and longer-term solutions: repurpose unused rooms, split oversized classrooms, extend the school day with staggered schedules, find nearby schools with available capacity for student redistribution, and identify funded SCA expansion projects and candidate sites for new construction. It also calculates the staffing needs (teachers and support staff) required for each solution, so administrators can cost and plan realistic interventions. All powered by real NYC Open Data.

How we built it

We built this through iterative refinement between human knowledge + prompting and AI-assisted implementation. Our teammate brought deep understanding of the school space problem; we used Claude Code to handle the data plumbing, API integration, and database patterns. Rather than asking for a complete rewrite upfront, we worked feature-by-feature: implement the space toolkit, test it against Satellite Academy HS (a real edge case), catch the bugs, iterate. This kept context manageable and forced us to verify each piece in isolation. The workflow was: specify what you want, Claude implement it, you test it against real data and catch what breaks, we fix it together. This "supervised AI coding" model worked well because we could catch hallucinations and semantic errors that would have slipped through without domain knowledge. For example, when Claude initially proposed a calculation for "permanent seats needed," we immediately spotted that it was making up fake numbers that were within a given range, something no amount of code review would catch without understanding the school problem itself.

Challenges we ran into

Managing AI hallucination and incomplete implementations. Claude would propose features with high confidence that turned out to be wrong on closer inspection. It suggested fuzzy name-matching for linking construction projects to schools, which would have worked most of the time but failed silently on edge cases—exactly the kind of bug that surfaces in production, not in testing. It also proposed netting incoming seat capacity against a school's shortfall, repeating a conceptual error you'd already corrected once. The solution was rigorous: never trust a feature description, always test against real data, and be skeptical of anything that sounds elegant but hasn't been verified. We built smoke tests for key queries (testing against schools with direct projects, schools with nearby projects, schools with no projects at all) and hand-verified calculations against real numbers. Token and prompt management. Early prompts were verbose and unfocused ("refactor the solutions panel"), which burned tokens and led to scope creep. We learned to be surgical: "delete this specific calculation" or "add this one field to the schema." We also learned that context explosions happen fast—rewriting everything at once fills the context window and increases hallucination risk. Breaking work into small, verifiable chunks (add the table, load the data, wire it into the component, test) kept things manageable. We also faced a hidden challenge: a recent pull switched the database driver from better-sqlite3 to node:sqlite, which broke your uncommitted capacity-projects code silently (no git merge conflict, just API calls to a method that no longer existed). This taught us that driver/API migrations need integration tests, not just unit tests, and that uncommitted work on a branch is fragile during major refactors.

What we learned

On the technical side, we learned a lot about building with public data infrastructure. We worked extensively with the Socrata API (things like pagination, rate limiting, snapshot queries, handling missing fields) and learned when to use live APIs versus periodic snapshots. We discovered that SQLite is excellent for this use case: we fetch data once, load it efficiently with WAL mode, then checkpoint it to a single self-contained file that serves read-only to the app. We also had to solve spatial problems (filtering schools within N miles using Haversine distance) and handle a database where one building houses multiple schools with competing space demands. On data quality, we learned that NYC's datasets are vast but messy. The COLP dataset publishes 97% bare street names with no house numbers—we had to add Borough-Block-Lot (BBL) identifiers and coordinates as workarounds. High school class size is reported as course sections, not headcount, which means a school can look severely overcrowded on paper when it's actually just scheduling too few, too-big classes. The newest class size data we have predates the class size mandate itself. These gaps forced us to build multi-source verification and special handling for different school types, but they also taught us that data quality, not code complexity, is often a bottleneck. On building with AI, we learned to be deeply skeptical. This was some of our group mates first times using vibe coding for large websites and tasks, so we didn’t always know what to expect from the output. The AI would hallucinate features and calculations confidently, proposing a "permanent seats needed" metric that turned out to conflate two incompatible constraints, or suggesting fuzzy name-matching that would accidentally connect Staten Island's "P.S. 13 ANNEX" to Brooklyn's P.S. 130 and other similar issues. We learned the hard way that every feature has to be tested against real data before we saved it and moved on. A calculation that looks correct on paper can be semantically broken in ways that only surface when you run it on actual schools.

What's next for ClassFit NYC

We're prioritizing stronger data inputs: post-mandate class size figures from NYCPS (currently using 2018-22 baselines), installed cafeteria seat counts, and SCA five-year pipeline details. Equally important is talking directly with school administrators to build features they actually need — case studies from pilot schools, integration with staffing and budget tools, and ways to surface the solutions in their existing workflows. Long-term, we're working toward official integration: partnering with NYC to surface compliance data in DOE dashboards and connecting directly to state reporting workflows. We also plan to expand the toolkit to cover transportation analysis, co-location dynamics, and special education space constraints.

Share this project:

Updates

Submission history