Platform Selection
Every alternative we looked at, what we picked, and why. Written so that we can reconstruct these decisions in two months when we have forgotten the arguments.
How we evaluated
The course asks for a two-step process, and we followed it deliberately. For each part of the platform we first wrote down the alternatives without judging them, so that we were not quietly defending a choice somebody had already made. Only then did we argue the trade-offs and pick.
Two of these are still open, and we say so below instead of pretending otherwise. The model provider and the hosting provider both depend on conversations with our client that have not happened yet. Everything else is decided.
Constraints from our client
Our client did not dictate a language, a framework, or a database, so most of this document is our own judgment. She did constrain us in four ways that shaped real decisions.
One further constraint comes from the course rather than the client: this has to be something she can keep running after we are gone. That weighs heavily in the hosting section below.
Programming languages
Phase one: alternatives considered
Against. Two toolchains to set up and keep running, not one.
Against. Gives up the ecosystem our team is most fluent in for the model-facing work.
Against. Means giving up React. Server-rendered templates are a poor fit for a chat interface that updates incrementally as an answer arrives.
Against. Nobody on the team works in them day to day. In a fourteen week project the learning curve is cost with no return.
Phase two: what we chose
Python, JavaScript, HTML, SQL. JavaScript and HTML because the product is a web application, SQL for retrieving data from the database, and Python because it is the language we are most comfortable with for interacting with AI agents. TypeScript comes along with the Drizzle decision below, since Drizzle is TypeScript first.
References: Python · TypeScript · pandas
Front end framework
Phase one: alternatives considered
Against. It is a library rather than a framework, so routing and state management are extra decisions we have to make ourselves.
Against. No one on the team has shipped anything in it.
Against. The steepest learning curve of the three, and far heavier than an application of this size needs.
Against. Smallest ecosystem here and no team experience, so every problem would be a first.
Against. We would end up hand-writing state management for a chat interface and an admin panel, which is the work a framework exists to remove.
Against. Our client asked for a responsive web interface, not something patrons have to install. Asking a 72 year old to find an app store listing defeats the purpose.
Phase two: what we chose
React on top of JavaScript and HTML. React's component model allows us to reuse UI elements across both the chat interface and the separate librarian admin view, while maintaining a responsive web interface across desktop and mobile browsers.
References: React · Vue.js · Angular · Tailwind CSS
Back end architecture and runtime
Phase one: alternatives considered
Against. Means our Python AI work either runs as a separate service or moves offline into the data pipeline.
Against. Loses the shared language with the front end, and rules out Drizzle, which is the ORM we judged best below.
Against. Cold starts work against our five second response target on an already slow model call, and the handoff story is more complicated for a non-technical client.
Against. A non-starter on security. It would put database credentials and model API keys in the browser where any patron could read them, and librarian permission checks would be trivially bypassed.
Phase two: what we chose
A client-server system. Client-server architecture is required to protect AI API keys and librarian credential checks. The application server is Node.js, which follows from choosing Drizzle and keeps the server in the same language as the front end.
Database
Phase one: alternatives considered
Against. Needs a hosted instance, so slightly more setup than a file-based database.
Against. Allows one writer at a time, which fits badly with a hosted admin panel that several librarians may use. Vector support is far less standard than pgvector.
Against. The data is already tabular with fixed columns, so the flexibility buys us little, and we would be writing application code to do joins the database should be doing.
Against. No equivalent to pgvector, and nothing it does better than Postgres for this workload.
Against. Document model again mismatched to tabular data, querying across 40 columns is awkward, and it is the deepest vendor lock-in here, which becomes a problem the day we hand the system over.
Phase two: what we chose
PostgreSQL. Postgres fits the client's tabular dataset structure, and keeping vector search in the same database through pgvector avoids standing up a second service that could drift out of sync with the records it describes.
References: PostgreSQL · pgvector · SQLite · MongoDB
Data access and ORM
Phase one: alternatives considered
Against. Younger than the alternatives, with a smaller community and fewer answered questions when we get stuck.
Against. Heavier runtime, and historically awkward when you need raw SQL or database extensions, which is our pgvector case.
Against. Weaker TypeScript support, which removes the main reason we wanted an ORM here at all.
Against. No type safety across 40 columns, and that is where our mistakes are going to happen.
Against. Only applicable under a back end we did not choose.
Phase two: what we chose
Drizzle ORM. Drizzle provides TypeScript-first type safety between the database and the application, rather than causing issues at runtime.
AI layer: retrieval and model provider
Phase one: alternatives considered
Against. Misses wording mismatches, where a patron says managing diabetes and the record says chronic disease resources.
Against. Adds an embedding step to the data pipeline that has to be rerun whenever the dataset is revised.
Against. Another service, another bill, and embeddings that can drift out of sync with the records. Heavy overkill for 467 rows.
Against. Ongoing cost, and patron health questions leave our client's control.
Against. Weaker instruction following, and a harder deployment and handoff story for a non-technical owner.
Phase two: what we chose
Open. The provider decision depends on the cost estimate we owe our client, and on whether she wants health questions to stay on her own hardware.
Retrieval is decided: structured filtering first, with pgvector embeddings layered on top for wording mismatches, and no external vector database. The model provider is not decided. We are keeping it behind one swappable interface so that the choice stays reversible, and we owe our client a cost estimate before anything paid is switched on.
Charting and data visualization
Phase one: alternatives considered
Against. Leaves librarians without a view of where the dataset is failing people.
Against. Less flexible than D3 once you want something unusual.
Against. Canvas based, so using it inside React means managing refs by hand.
Against. Enormously more power than a frequency bar chart justifies, and the steepest learning curve here.
Phase two: what we chose
None required for the user end. We may implement light metric visualizations in the librarian admin panel to highlight unanswered questions and uncover dataset gaps. If time permits, this will be built using a React charting library like Recharts or Chart.js. We are not processing images or video at all; the dataset is text, URLs, and metadata.
Hosting, handoff, and fees
The course has historically defaulted to Heroku. That is no longer a free option, so we evaluated this from scratch.
Phase one: alternatives considered
Against. Heroku ended its free product plans in November 2022. Every dyno and every Postgres instance now costs money from day one, which makes it a poor fit for a project we have to hand to a client who may not want a recurring bill.
Against. Free instances sleep when idle, so the first request after a quiet period is slow.
Against. Free usage is credit based rather than a standing free tier, so an idle project can still accrue cost.
Against. More infrastructure knowledge required than the others, which hurts the handoff.
Against. Splits the system across two vendors and two dashboards, which is one more thing to explain at handoff.
Against. Someone has to administer it. Our client's group does not have staff for that, and it would not outlive us either.
Against. Vastly more infrastructure than a four person semester project needs, and completely unmaintainable by our client afterwards.
Phase two: what we chose
Open. Pending a conversation with our client about long term cost and who maintains the deployment after handoff.
Not yet decided. Our shortlist is Render, or Vercel paired with Neon or Supabase, because both keep the project inside a free tier during development and both support pgvector. The deciding factor is not developer convenience but handoff: this system has to keep running after we leave, so we need to know from our client whether she can carry a small recurring cost or whether a free tier is a hard requirement. That is on the agenda for our next client meeting.
References: Heroku · Render · Railway · Fly.io · Vercel · Neon · Supabase
Target environment
Phase one: alternatives considered
Against. We have to test across more browsers and screen sizes than a single-target app would.
Against. Two more codebases, an install step our users will not take, and no requirement from the client for it.
Against. One of our four personas is a teenager with nothing but a phone.
Phase two: what we chose
Modern desktop browsers such as Chrome, Safari, Firefox, and Edge, as well as mobile web environments such as Safari on iOS and Chrome on Android. No specific hardware or operating system. We are targeting WCAG 2.1 AA throughout, which is a requirement for us, not an aspiration, given our personas.
References: WCAG 2.1