F26 · COMP 523

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.

The dataset arrives as a spreadsheet, and it keeps changing
Roughly 467 rows and about 40 columns, delivered on OneDrive, still being corrected by her research students. Loading it therefore has to be a repeatable job, and our database schema has to tolerate columns being revised under us.
She has offered hardware with a local model on it
This is a real alternative to a paid API, not a hypothetical, and it is the main reason we are keeping the model behind a swappable interface instead of writing one provider's SDK through the codebase.
She will fund API costs, but wants a justified estimate first
Cost is a live constraint on the AI layer, not an afterthought. We owe her that estimate, and we cannot write it until the provider question is settled.
Librarians must be able to govern the data
This forces authentication, an audit log, and an override path into the design. It is why a purely static or browser-only architecture was never an option.

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

Selected
Python, JavaScript, HTML, SQL
For. Puts each language where it is strongest. Python is where our team is most comfortable working with AI agents and has the most mature libraries for it. SQL follows directly from the data already being tabular.
Against. Two toolchains to set up and keep running, not one.
JavaScript everywhere, no Python
For. One language across the whole project, the smallest possible setup, and one dependency manager.
Against. Gives up the ecosystem our team is most fluent in for the model-facing work.
Python everywhere, with Django or Flask templates
For. A single language, and the framework handles routing and admin scaffolding for free.
Against. Means giving up React. Server-rendered templates are a poor fit for a chat interface that updates incrementally as an answer arrives.
A statically typed language such as Java or C#
For. Strong typing and very mature tooling.
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

Selected
React
For. It is what our team is most familiar with, and over fourteen weeks that counts for more than it sounds like. Its component model lets us reuse interface pieces across both the chat view and the librarian admin view. Largest ecosystem of the options here.
Against. It is a library rather than a framework, so routing and state management are extra decisions we have to make ourselves.
Vue.js
For. Gentler learning curve than React and more included out of the box.
Against. No one on the team has shipped anything in it.
Angular
For. Opinionated and complete. Routing, forms, and HTTP all come with it, and TypeScript is the default.
Against. The steepest learning curve of the three, and far heavier than an application of this size needs.
Svelte
For. Less boilerplate and smaller bundles, which is attractive given our slow-connection requirement.
Against. Smallest ecosystem here and no team experience, so every problem would be a first.
No framework, plain JavaScript and HTML
For. Zero dependencies and the smallest possible payload for rural connections.
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.
React Native
For. Would give us real native mobile applications.
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

Selected
Client-server, with a Node.js application server
For. Keeps one language across the front end and the server. Required anyway, since our model API keys and the check for whether somebody is actually a librarian cannot live in the browser. Pairs naturally with Drizzle.
Against. Means our Python AI work either runs as a separate service or moves offline into the data pipeline.
Client-server, with a Python back end such as FastAPI or Django
For. Keeps the AI work in the language our team prefers for it, in a single service.
Against. Loses the shared language with the front end, and rules out Drizzle, which is the ORM we judged best below.
Serverless functions
For. No servers to manage and a generous free tier on most providers.
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.
Static site talking to the database directly from the browser
For. Simplest possible deployment, and no server to pay for.
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.

References: Node.js · Express · Auth.js

Database

Phase one: alternatives considered

Selected
PostgreSQL
For. Relational, which matches a dataset that is already a table with fixed columns. Handles the ZIP code to county to library system to outlet relationships as ordinary joins. The pgvector extension gives us vector search later without adding another service. Free managed tiers exist.
Against. Needs a hosted instance, so slightly more setup than a file-based database.
SQLite
For. No setup at all, the whole database is one file, and 467 rows is nothing to it. We argued about this one.
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.
MongoDB
For. Flexible schema, which is appealing while the client's columns are still being revised.
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.
MySQL
For. Mature, extremely widely hosted, and well understood.
Against. No equivalent to pgvector, and nothing it does better than Postgres for this workload.
Firebase or Firestore
For. A full platform with authentication and hosting included, and it has served past COMP 523 teams well.
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

Selected
Drizzle ORM
For. TypeScript first, so the roughly 40 fields on every record stay type checked between the database and the application, so mistakes surface before they ship. Generates plain readable SQL, and we will need that for pgvector operators and hand written queries.
Against. Younger than the alternatives, with a smaller community and fewer answered questions when we get stuck.
Prisma
For. The most mature option, excellent developer experience, and migrations are a solved problem.
Against. Heavier runtime, and historically awkward when you need raw SQL or database extensions, which is our pgvector case.
Sequelize
For. Long established and very widely deployed.
Against. Weaker TypeScript support, which removes the main reason we wanted an ORM here at all.
Raw SQL through node-postgres
For. Total control, no abstraction to fight, and nothing to learn.
Against. No type safety across 40 columns, and that is where our mistakes are going to happen.
SQLAlchemy
For. The obvious choice if the back end were Python.
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.

References: Drizzle · Prisma · Sequelize

AI layer: retrieval and model provider

Phase one: alternatives considered

Structured SQL filtering with Postgres full text search
For. At 467 rows, filtering to the user's own library system already narrows the candidates to a handful. Costs nothing and adds no dependency.
Against. Misses wording mismatches, where a patron says managing diabetes and the record says chronic disease resources.
Embeddings stored in pgvector, on top of that filtering
For. Handles the wording mismatch. Lives in the database we already have, so a librarian's edit and its re-embedding happen together.
Against. Adds an embedding step to the data pipeline that has to be rerun whenever the dataset is revised.
A dedicated vector database such as Pinecone or Chroma
For. Purpose built, and scales far past anything we need.
Against. Another service, another bill, and embeddings that can drift out of sync with the records. Heavy overkill for 467 rows.
A hosted model API
For. Best instruction following, which is what our guardrails actually depend on. The assistant has to cite sources and has to be willing to say it does not know, and hosted models are more reliable at that.
Against. Ongoing cost, and patron health questions leave our client's control.
An open-weight model run locally on our client's hardware
For. No per-query cost at all, and nothing sensitive leaves her machine. She has already offered us the laptop.
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.

References: pgvector · Chroma · Pinecone · Ollama

Charting and data visualization

Phase one: alternatives considered

Selected
No charting at all
For. Nothing in the patron interface needs it. It is a conversation plus a list of resources.
Against. Leaves librarians without a view of where the dataset is failing people.
Recharts
For. React components, so it drops straight into our stack with no wrapper.
Against. Less flexible than D3 once you want something unusual.
Chart.js
For. Small, fast, and very well documented.
Against. Canvas based, so using it inside React means managing refs by hand.
D3
For. Can draw anything.
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.

References: Recharts · Chart.js · D3

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

Heroku
For. The course's traditional choice, well documented, and simple to deploy to.
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.
Render
For. Free tier for web services, managed Postgres, and a deployment model close to what Heroku offered.
Against. Free instances sleep when idle, so the first request after a quiet period is slow.
Railway
For. Very fast to set up, and the database and application sit together.
Against. Free usage is credit based rather than a standing free tier, so an idle project can still accrue cost.
Fly.io
For. Runs close to users and handles persistent services well.
Against. More infrastructure knowledge required than the others, which hurts the handoff.
Vercel plus a managed Postgres provider such as Neon or Supabase
For. Strong free tiers on both halves, trivial deployment, and Neon and Supabase both support pgvector.
Against. Splits the system across two vendors and two dashboards, which is one more thing to explain at handoff.
A university-hosted or client-hosted server
For. No commercial dependency, and the client keeps full control of patron data.
Against. Someone has to administer it. Our client's group does not have staff for that, and it would not outlive us either.
Docker and Kubernetes
For. Portable and the industry standard at scale.
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

Selected
Responsive web, all current browsers
For. One codebase reaches every device our personas actually use. Nothing to install, and an older patron is not going to go hunting through an app store.
Against. We have to test across more browsers and screen sizes than a single-target app would.
Native mobile applications
For. Better offline behaviour and a presence in the app stores.
Against. Two more codebases, an install step our users will not take, and no requirement from the client for it.
Desktop only
For. Simplest to build and test.
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