DEV Community

Cover image for Todo Lists Organize Information. I Built an App That Organizes Time.
Meir Vaknin
Meir Vaknin

Posted on

Todo Lists Organize Information. I Built an App That Organizes Time.

It started in a cafeteria

Three of us were sitting in the college cafeteria, complaining about the same thing.

And it wasn't that we couldn't find our information. Every college has an app now. You log in and there it is: your timetable, your exam dates, your grades, the assignment deadlines out of Moodle. All of it, in one place, up to date.

All three of us were still constantly behind.

Because that app tells you an assignment is due on the 14th. It doesn't tell you that Sunday evening is free and that's when you should be writing it. It reports. It doesn't plan. And underneath the whole semester sits the constant low-grade certainty that something is slipping, and you don't know what yet.

We weren't short on information, and we all had todo apps on top of it. The problem was a different one entirely: not one of us could answer this question.

What should I be working on right now, and when do I actually have time for the rest of it?

That question became Focusity.

Illustration of a student at a laptop surrounded by calendar, notes and book icons

The pitch, in Hebrew: "Your studies, finally organized."

The distinction that shaped everything

It would have been very easy to build another task manager. Courses, assignments, exams, a nice calendar, done.

But as I said, the information was never the missing piece. The college app already had it, and had it accurately. Even with everything visible in one place, I still didn't know what to do with Tuesday afternoon.

So I drew a line early and never crossed it:

  • A todo list organizes information.
  • Focusity organizes time.

Everything downstream came from that. If the product's job is to decide when, then the calendar stops being a display surface and becomes the output. The scheduling engine stops being a feature and becomes the product. And a task the user has to drag onto a free slot by hand is a failure.

That one sentence saved me from about ten features I would otherwise have built.

The MVP call: starting on a platform I knew I'd leave

I started Focusity on Base44, an app-building platform.

I didn't know yet whether the core idea worked, or whether students would even bother importing their schedule. Spending three weeks on auth, database schemas and deploy pipelines before validating any of that would have been optimizing the wrong thing.

What I bought: a working product in days instead of weeks, and fast iteration on flows while the product definition was still moving. What I paid: a ceiling I couldn't see yet.

I'll come back to what happened when I hit it.

Designing before building

Almost every screen in the app has a Figma design behind it. That wasn't process worship, it was arithmetic. Redrawing a screen in Figma costs minutes. Rebuilding it in React costs hours and leaves scar tissue in the codebase. When the UI is dense with information, like a week grid or a dashboard that has to answer "what now?" at a glance, getting the hierarchy wrong is expensive.

There's a branch in my git history called feature/match-figma-pages, and a commit titled "checkpoint before Figma-based calendar/menu QA pass". When the code and the design drifted apart, the code is what moved. That's the difference between having designs and treating the design as the spec.

Feature board showing smart timetable, tasks, exam reminders, grade tracking and automatic daily planning

The feature set, framed around student moments rather than modules.

The four layers

Focusity is four layers stacked, and almost every feature belongs to exactly one of them.

   INTAKE                    MODEL                PLANNING              FOLLOW-UP
   ──────                    ─────                ────────              ─────────
   Moodle ICS  ─┐
   CSV / Excel ─┤                              ┌─ auto-scheduler ─┐
   Exam PDF    ─┼──▶  courses · tasks   ──────▶├─ exam prep       ├──▶  analytics
   Grade PDF   ─┤      exams · time blocks     └─ catch-up board ─┘     grades
   Schedule PDF─┤      grades                                            reminders
   Google Cal  ─┘
                        ▲                                                    │
                        └──────────── velocity feedback ─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Intake is where most study planners quietly die. Nobody hand-types 22 courses and a semester of class times. So Focusity imports: an ICS feed from Moodle, a CSV or Excel timetable, a PDF exam schedule. Google Calendar connects with two-way sync, so commitments that already live there show up in the plan.

Model is one normalized set of entities, so everything downstream reasons over the same data regardless of where it came from. Planning is the engine, covered in the next section. Follow-up closes the loop: what got done, what slipped, how accurate the estimates were.

The Courses screen listing import options: Moodle ICS, CSV/Excel timetable and exam PDF

Import paths: Moodle ICS, CSV/Excel, exam PDF. Everything routes through a review screen first.

The engine

Given tasks with deadlines and a calendar that's already partly full, place study time. Sounds simple. It isn't, because free time is only the beginning of the constraints.

Hard vs. soft constraints. A class is immovable. So is an exam, and so is a work shift. The only blocks the scheduler may move are the task-study blocks it placed itself on a previous run. Everything else on the calendar, including its own exam-revision blocks, is busy time it plans around. This one rule is what makes re-running it safe: it can never quietly delete something you put there yourself.

Pomodoro splitting. Work is carved into 50-minute focus blocks with 10-minute breaks. Pomodoro isn't magic, but "study for 4 hours" is a lie you tell yourself, and "four 50-minute blocks" is a thing that actually happens.

Eat That Frog. Demanding tasks (high importance, high difficulty, or high energy) claim the earliest slots inside your peak productivity window. You declare that window in settings: Morning, Afternoon, Evening or Night.

That setting had a wrinkle worth mentioning. Originally the planner stopped at 22:00 for everyone, which made Night nearly identical to Evening: a dropdown option that changed nothing. So Night, and only Night, extends the planning window to midnight. If you explicitly told the app you work at night, it should believe you. If you didn't, it should never push a block to 23:30.

Load balancing. A task's blocks spread across the days remaining until its deadline instead of piling into one. A second pass lifts the per-day cap for whatever didn't fit, so nothing is silently dropped. It just lands less ideally.

Personal velocity. My favorite piece. Every session records planned duration and actual duration. After three completed sessions the app computes the ratio and calibrates future estimates, smoothed so one disastrous Tuesday doesn't reset the model.

The effect: if you consistently need 90 minutes for what you estimate at 60, Focusity stops taking your estimates at face value and reserves what you actually need. You don't have to get better at estimating. The system gets better at estimating you.

Priority itself is a plain, boring formula:

export function calcPriorityScore(task, courseBoostMap = {}) {
  const diff = task.difficulty_level || 3;
  const imp  = task.importance_level || 3;
  const deadline = new Date(task.deadline);
  const hoursLeft = Math.max(differenceInHours(deadline, new Date()), 1);
  const urgency = Math.min(10, 100 / hoursLeft);
  const boost = (task.course_id && courseBoostMap[task.course_id]) || 1;
  return Math.round(((diff + imp) / 2) * urgency * boost * 10) / 10;
}
Enter fullscreen mode Exit fullscreen mode

That boost is where grades feed back in: a course whose current grade sits below a set threshold raises the priority of its tasks, scaled by how many credits it carries.

Grades aren't a report card in Focusity. They're an input. The same data drives a "what if I retake this" projection that recomputes your credit-weighted average across up to seven courses at once. The question stops being should I retake this course and becomes which combination moves my average the most, for the least work.

The dashboard showing a study streak, today's plan and the most urgent task

"Most urgent" surfaced up top, today's plan below, one button to reschedule everything.

A week calendar grid with class and study blocks laid out across five days

The plan is the calendar. There is no separate "plan" screen to go read.

Why the engine is not AI

The obvious assumption in 2026 is that an LLM does the scheduling. It doesn't, and that's the most opinionated decision in the app.

The scheduler is deterministic, transparent and fast. You can always see why a block landed where it did: deadline, difficulty, importance, your declared peak hours, the free time you actually had.

An LLM would be worse here on every axis I care about. It's slower, and it's non-deterministic, so re-running the same plan gives a different answer for no reason you can explain. Most importantly, a student who doesn't trust the plan won't follow it, and "the AI decided" is not a reason anybody trusts.

There's also a practical consequence. The planners are pure functions: same inputs, same output, no I/O, no hidden clock. Even "now" is passed in. That's what makes them testable. The exam planner has a standalone harness that hands it a synthetic calendar and asserts the plan that comes back, block by block. You cannot write that test against a language model.

I did build a conversational planning agent at one point. It's still in the codebase, disconnected from navigation, and any message sent to it returns a maintenance notice. That's the honest state of that feature: it doesn't do what its interface promises, so it doesn't get to sit in the nav pretending otherwise. A feature that doesn't work is worse than a feature that doesn't exist, because the first costs you trust and the second costs you nothing.

Trust as a design constraint

Once the product is "an engine makes decisions for you," trust stops being a nice-to-have and becomes the thing you're building.

Every import goes through a review screen. ICS, CSV and PDF imports never write to your calendar directly. You see the parsed rows, fix what's wrong, approve. Parsers fail and university PDFs are chaotic. The answer is to never let a parser have the last word.

The exam planner refuses to lie. When you set up a preparation plan, it checks whether the work genuinely fits in the free time before the exam. If it doesn't, it says so and reports the shortfall in minutes. Silently truncating the plan would have been easy, and that's the version that gets you a bad grade and a user who never comes back.

Nothing missed disappears. Everything you skipped lands on one catch-up board, each item paired with a concrete new slot the engine already found in your real calendar. One tap to reschedule. The failure mode I was designing against is the one I know personally: you fall behind, the backlog goes shapeless, and you stop opening the app.

The catch-up board listing missed items, each with a suggested new time slot

13 missed items, each with a real free slot already found.

Then I hit the ceiling

About four months in, Base44 stopped being an accelerant.

The breaking points were data access and auth: I needed queries the platform's data layer didn't express, background jobs, and control over the authentication flow rather than the one I was handed. Every workaround was making the codebase worse in a way that would eventually cost more than a migration.

So in a single day in July I moved everything: auth to Supabase, then a schema migration replacing the platform's entities with seven real Postgres tables, then every page and component off the platform's data layer. That schema has since grown to 24 tables across 31 migrations, plus Edge Functions and cron jobs that simply weren't possible before. The migration surfaced an auth-flow deadlock and a fistful of data-layer bugs the platform had been papering over. Painful week. Correct decision.

The takeaway I'd defend in a room: a good MVP decision includes the condition under which you'll abandon it. Base44 did exactly what I chose it for. Staying out of sunk cost would have been the actual mistake, and leaving on schedule was the plan working rather than failing.

Today: React + Vite on the front end, Supabase for Postgres, auth, Edge Functions and cron, Vercel for hosting, a small FastAPI service on Render for PDF parsing, and an Expo shell wrapping the web app for the stores.

Three ways to sign into the same app

Google and Apple sign-in is one line on a roadmap and three implementations in the code: one for the browser, one for the native shell, one for the WebView bridge. Inside a WebView you have no access to the Google session already signed in on the device, so instead of "continue as…" the user gets an empty login form every single time.

And Sign in with Apple isn't a design choice at all: App Store guideline 4.8 requires it once you offer third-party login. Some of your architecture gets written by someone else's policy document.

Where reality pushed back

Rejected by the App Store. Guideline 4.5.4: push notifications can't be on by default. Fair, and fixed by making them opt-in.

Hebrew comes out of PDFs backwards. Right-to-left text arrives reversed from table extraction and has to be reconstructed before anything can be parsed. The service finds the header row by scoring rows against Hebrew keywords and maps columns dynamically. That's what lets one parser handle exports from different institutions without hardcoding column positions.

A native rewrite that didn't ship. I got three phases into a React Native port (scaffold, business logic, UI) before concluding the WebView shell would reach students months sooner at a fraction of the maintenance cost. I deleted the port. Shipping beat purity.

Where it stands

Focusity is live on the App Store. The Android build is in testing with testers, not publicly available yet. It works in any browser, mobile-first, and it's free. There's no paid plan today and no card.

It has real early users. Not many. Their feedback has been positive on the idea and the design, and I want to be precise about what that is and isn't: informal feedback from a small group, not user research, with no usage metrics behind it. What it did was confirm the problem is real and the direction is right. The next step is widening that circle and collecting feedback in a structured way, so prioritization runs on something better than my own judgment.

The analytics screen showing a productivity score, weekly study hours and achievement badges

A productivity score combining estimation accuracy, focus and consistency, along with XP, streaks and streak freezes.

How I actually worked

Semester load meant Focusity moved slowly for months. Since finishing the semester, and while doing reserve duty a week on and a week off, it's had real focused time. The git history shows it honestly: two near-silent months in late spring, then just under 300 commits in July.

The loop stayed the same throughout: define a problem, map the flow, build a version, use it, find what's unclear or broken, improve it. That's not a methodology I read about; it's what happens when you're your own first user.

Every change ships through a branch and a pull request, and every schema change is a numbered, reviewable migration rather than a hand-edit against the database. There are 31 of them so far. That's not ceremony for a solo project. It's what lets me look back and see exactly when a decision was made and what it changed.

And yes, I used AI tooling throughout, including Claude Code, for building, testing, iterating and debugging. What it did was compress the distance between "I know what this should do" and "this works." What it didn't do was decide what the problem was, who it's for, what the engine optimizes for, which features to cut, or when a plan is honest.

There's a symmetry there I only noticed later. I use AI as a development tool and refuse to use it as the product's decision-maker, for the same reason in both directions. The judgment has to be attributable to someone. In the code, that's me. In the app, it's a transparent engine the student can inspect and overrule.

Back to the cafeteria

That cafeteria conversation got one thing exactly right: the problem was never that we didn't know what we had to do. It was that nobody could tell us when.

That's a scheduling problem, not a note-taking problem. Once you see it that way, you build something different.


Focusity: App Store · focusity.co.il

Hebrew-first, RTL from the ground up, free while I grow it. If you're a student, or you build for them, I'd genuinely like to hear what you think.

Top comments (0)