From fbc528aa569e313af083be7601766db60537568b Mon Sep 17 00:00:00 2001 From: ShaunvdBijl Date: Tue, 19 May 2026 10:03:14 +0200 Subject: [PATCH 1/8] Completed Exercise 1 --- documentation.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 documentation.md diff --git a/documentation.md b/documentation.md new file mode 100644 index 00000000..e69de29b From fc86931f1ad8b72b7d5f6ab48f30c1a28492c56a Mon Sep 17 00:00:00 2001 From: ShaunvdBijl Date: Tue, 19 May 2026 10:28:41 +0200 Subject: [PATCH 2/8] Completed Exercise 2 --- documentation.md | 0 .../task-manager/javascript/documentation.md | 146 ++++++++++++++++++ .../task-manager/javascript/newFeature.md | 118 ++++++++++++++ 3 files changed, 264 insertions(+) delete mode 100644 documentation.md create mode 100644 use-cases/task-manager/javascript/documentation.md create mode 100644 use-cases/task-manager/javascript/newFeature.md diff --git a/documentation.md b/documentation.md deleted file mode 100644 index e69de29b..00000000 diff --git a/use-cases/task-manager/javascript/documentation.md b/use-cases/task-manager/javascript/documentation.md new file mode 100644 index 00000000..1e7c51af --- /dev/null +++ b/use-cases/task-manager/javascript/documentation.md @@ -0,0 +1,146 @@ +# Exercise 1 Code Understanding + +## Language Used +The code I have chosen is **JavaScript**. + +# Code Breakdown + +## Codebase + +There are 4 javascript files it appears. +There seems to be an attempt at a backend by using module.export and the require key word and file paths but no official frameworks. + +## Initial Understanding +### Organization +- It appears app.js is the main file. +- cli.js seems to convert the project into a usable command line interface by creating a program constant and using the commander library to create a command line interface. +- models.js seems to be handeling the task management of the project +- storage.js seems to be running where and how the project data is stored. It can also edit the stored data. + +### Frameworks and technologies +- module exports +- CLI tools/commander library +- Task manager class +- Engine uuidv4 +- File pathing + +### Main Components +- CLI Tools +- File pathing + +## Project Structure +### Using Prompt +*** My Prompt *** +I'm a junior developer who just joined this project. I've read the README but still need help understanding the project structure and technology stack +Here's my current understanding of the project: + +## Codebase + +There are 4 javascript files it appears. +There seems to be an attempt at a backend by using module.export and the require key word and file paths but no official frameworks. + +## Initial Understanding +### Organization +- It appears app.js is the main file. +- cli.js seems to convert the project into a usable command line interface by creating a program constant and using the commander library to create a command line interface. +- models.js seems to be handeling the task management of the project +- storage.js seems to be running where and how the project data is stored. It can also edit the stored data. + +### Frameworks and technologies +- module exports +- CLI tools/commander library +- Task manager class +- Engine uuidv4 +- File pathing + +### Main Components +- CLI Tools +- File pathing +Project structure +task-manager + --Java + --Javascript + --app.js + --cli.js + --models.js + --storage.js + --python +We are only focussing on the JavaScript folder +Configuration Files: +None +Could you: +1. Validate my understanding and correct any misconceptions +2. Identify additional key technologies, frameworks, and libraries used +3. Explain what each main folder likely contains and its purpose +4. Point out where the application entry points are located +5. Suggest 3-5 specific questions I should ask my team to deepen my understanding +After your explanation, could you suggest a small exploration exercise I could do to verify my understanding of the project structure? + +*** Claude Response *** +1. Validating Your Understanding +Your instincts are largely correct, with a few refinements: +What you got right: + +app.js as the main entry point is a standard Node.js convention — good call. +cli.js using Commander to expose a CLI is accurate. Commander is a well-established library for parsing command-line arguments and defining commands/flags. +storage.js handling data persistence (read/write) is a sensible separation of concerns. +models.js containing a Task management class is a classic MVC-style "model" layer. + +Misconceptions to correct: + +"An attempt at a backend" — module.exports and require are not signs of an incomplete or attempted backend. They are simply Node.js's built-in CommonJS module system, the standard way to split code across files in Node.js. This is intentional and complete, not a workaround. +"No official frameworks" — this is likely a deliberate architectural choice. Many CLI tools are built as lightweight Node.js apps without Express or similar, because they don't serve HTTP traffic. The absence of a framework isn't a gap; it's appropriate for the tool's purpose. +"Engine uuidv4" — UUIDv4 is not an engine. It's a small utility library for generating universally unique IDs (e.g. giving each task a unique identifier like f47ac10b-58cc...). Think of it as a helper, not a runtime. + + +2. Key Technologies & Libraries +Based on your description, here's the fuller picture: +TechnologyRoleNode.jsThe JavaScript runtime — what actually executes the codeCommonJS (require/module.exports)Node's module system for splitting code across filesCommander.jsParses CLI arguments, defines commands and flagsuuid / uuidv4Generates unique IDs for each taskNode fs moduleAlmost certainly used in storage.js for reading/writing files to diskJSONLikely the storage format (a flat .json file acting as a simple database) + +3. What Each File Likely Does +task-manager/ +└── Javascript/ + ├── app.js → Wires everything together. Imports the CLI and kicks off execution. + ├── cli.js → Defines user-facing commands (e.g. `add`, `list`, `delete`). + │ Uses Commander to parse what the user types in the terminal. + ├── models.js → The Task class/object. Defines what a task looks like (id, title, + │ status, etc.) and methods to operate on tasks. + └── storage.js → Reads from and writes to a file on disk. Handles all persistence + so the other files don't need to know *how* data is saved. +The Java and Python folders likely contain parallel implementations of the same task manager — a common pattern for learning projects or polyglot codebases demonstrating the same app in multiple languages. + +4. Entry Points +There are likely two entry points depending on how the app is used: + +app.js — the primary Node.js entry point, probably run via node app.js or referenced in a package.json "main" field. +cli.js — may also be executable directly (e.g. node cli.js add "Buy milk"), or it's imported by app.js which delegates to it immediately. + +Check for a package.json — if it exists, the "bin" or "main" field will tell you the definitive entry point, and "scripts" will show you how the team actually runs it. + +5. Questions to Ask Your Team + +"Is there a package.json, and what does the scripts section look like?" — This reveals how the app is built, run, and tested, and whether there are dependencies you haven't seen yet. +"Where is task data actually stored — a local JSON file, or something else?" — Understanding the storage target clarifies storage.js's full role. +"Is this CLI tool meant to be installed globally (npm install -g) or run locally?" — This affects how users interact with it and how Commander is configured. +"Are the Java and Python folders feature-equivalent to the JavaScript version, or are they experiments/stubs?" — Knowing this tells you whether cross-language consistency matters. +"Are there any plans to add tests?" — Junior-friendly question that reveals team practices and gives you a contribution opportunity. + + +Exploration Exercise +Try this structured trace-through — no code changes, just reading: + +Start at app.js — identify every require() call. Draw a simple arrow diagram of what it imports. +Open cli.js — find each .command() call in Commander. List the commands the app exposes (e.g. add, list, complete). +Open models.js — find the class definition. List its properties (what fields does a Task have?) and its methods. +Open storage.js — find where it reads and writes. What filename or path does it use? What format is the data? +Try running it — if Node.js is installed, run node app.js or node cli.js --help and see if the Commander help output matches the commands you found in step 2. + +## Findings +### Misconceptions to correct: +1. "An attempt at a backend" — module.exports and require are not signs of an incomplete or attempted backend. They are simply Node.js's built-in CommonJS module system, the standard way to split code across files in Node.js. This is intentional and complete, not a workaround. +2. Engine uuidv4" — UUIDv4 is not an engine. It's a small utility library for generating universally unique IDs (e.g. giving each task a unique identifier like f47ac10b-58cc...). +### Final Resault +1. My understanding of the project structure is mostly correct, with some refinements regarding the use of CommonJS modules and the role of uuidv4. +2. Key technologies include Node.js, CommonJS modules, Commander.js for CLI parsing, uuid +3. Each file has a clear purpose: app.js is the main entry point, cli.js defines the CLI commands, models.js defines the Task class, and storage.js handles data persistence. +4. The main entry point is likely app.js, but cli.js may also be directly executable \ No newline at end of file diff --git a/use-cases/task-manager/javascript/newFeature.md b/use-cases/task-manager/javascript/newFeature.md new file mode 100644 index 00000000..d9b446e8 --- /dev/null +++ b/use-cases/task-manager/javascript/newFeature.md @@ -0,0 +1,118 @@ +# Exercise 2 +Add task: Export to CSV +## Initial Search +No similar feature was found to convert a file to export + +## Hypothesis +I think the best place to use this feature would be in cli.js as it deals with the terminal mostly so it easily accessable to this applications and future implements. The terminal also has easy access to fiels so the conversion to csv would be easier to implement in this file. + +**no files need adjustment** + +### Search Terms +- "export to csv" +- "convert to csv" +- "exports" +- "csv" + +## Feature Location Prompt + +### Prompt Used +I need to work on adding a task: Convert to CSV in this codebase, but I'm not sure where the code for this feature lives. + +My approach so far: +- I've searched for keywords like "export to csv", "convert to csv", "exports", "csv" +- I looked in cli.js which seemed relevant +Can you help me: +1. Evaluate my search approach and suggest improvements +2. Identify which files and directories most likely contain the implementation for this feature +3. Suggest specific search terms or patterns that would be more effective +4. Explain what parts of the feature might be located in different areas of the codebase +5. Recommend a step-by-step investigation process to understand the complete feature flow + +Also, what questions could I ask myself as I'm exploring the code to ensure I'm on the right track? What specific patterns should I look for to confirm I've found all the relevant parts? + +After your guidance, could you give me a small challenge to test my understanding of how to navigate this feature's code? + +### Prompt Response +Comprehensive Navigation Guide for the CSV Feature +1. Evaluation of Your Search Approach ✓ +What You Did Well: + +Searched for direct keywords ("export to csv", "convert to csv") +Looked at cli.js (correct instinct—UI layer often reveals exposed features) +What Could Be Improved: + +Your search was feature-focused rather than architecture-focused—CSV export doesn't exist yet +Better approach: First understand the code structure, then search strategically +You correctly hypothesized cli.js but didn't yet map where the CSV conversion logic belongs +2. Files & Directories Most Likely to Contain CSV Logic +File Role Expected Changes +cli.js Command-line interface Add export or export-csv command +storage.js Persistence/file I/O Add exportToCSV() method (follows existing pattern) +app.js Business logic Optional: add exportTasks() method +models.js Data definitions No changes needed (already has Task structure) +3. Better Search Terms & Patterns +Instead of searching for "csv", try these sequences: + +4. How the Feature Flows Across the Codebase +5. Step-by-Step Investigation Process +Phase 1: Understand Data Structure + +Read models.js → What fields does a Task have? +Read storage.js → How are tasks stored/retrieved? (Look for getAllTasks()) +Trace: Where does task data originate and how is it accessed? +Phase 2: Identify Integration Points +4. Read cli.js → How are existing commands structured? (Look for program.command() patterns) +5. Compare: What do list, create, delete commands do? +6. Pattern Match: Find similar file-writing operations if any exist + +Phase 3: Locate Persistence Patterns +7. In storage.js, find the save() method → This is the model for CSV export +8. Question: Does save() use fs.writeFileSync()? Yes → Use same approach + +Phase 4: Determine Ownership +9. Is CSV logic belongs in storage.js (file I/O) or app.js (business logic)? +10. Check: Does app.js expose an getAllTasks() or similar public method? + +6. Questions to Ask While Exploring +Ask yourself these during code exploration: + +"Who calls this function?" → Trace backwards to see dependencies +"What data does this return?" → Understand the shape of data flowing through +"Where is file I/O happening?" → Look for fs. operations +"What's the pattern for similar features?" → Compare with list, stats, delete commands +"Which layer handles each responsibility?" → CLI? Business Logic? Storage? +"Is there a public API I should use?" → Via TaskManager or TaskStorage? +"What error cases exist?" → File permissions, invalid paths? +7. Patterns to Confirm You've Found Everything +✓ You've found it all when you can answer: + +Data Source: "I can get all tasks via this.storage.getAllTasks()" +Transformation: "I can convert task objects to CSV using headers + rows" +Output: "I can write to file using fs.writeFileSync() like the save() method does" +Integration: "I can add a new CLI command in cli.js like the stats command" +End-to-End: "User runs export-tasks --output file.csv → triggers handler → converts tasks → writes file" +8. Your Challenge 🎯 +Task: Without looking at any code, answer these: + +Data Flow: Where would you retrieve the task data for CSV export—from TaskStorage.getAllTasks() or from TaskManager.listTasks()? Why? +File Writing: Which method in storage.js shows you the exact pattern for writing files? Find it and identify the 2-3 lines you'd need to adapt. +CLI Integration: In cli.js, find one existing command and describe what happens in its .action() callback. +Ownership: Should the CSV formatting logic live in storage.js or a new separate file like formatters.js? Justify your answer. +Completeness Check: What would need to change in app.js to support taskManager.exportToCSV(filename)? +Try these now: Read just storage.js carefully and answer questions 1-2. Then read just one command in cli.js and answer question 3. Post your answers and I'll validate your understanding! + +## Findings +### Where to implement the feature +1. CLI Layer (cli.js) — Add a new command (e.g. export-tasks) that users can run from the terminal. +2. Storage Layer (storage.js) — Implement the actual logic to convert tasks to CSV format and write to a file. This keeps file I/O concerns separate from business logic. +3. Optional: Business Logic Layer (app.js) — If you want to keep CLI thin, you could add an exportTasks() method here that orchestrates the retrieval and formatting, but it's not strictly necessary. +### Related components to be affected +- cli.js — for the new command and its handler +- storage.js — for the CSV conversion and file writing logic +### Plan to implement export feature +1. In cli.js, define a new command (e.g. `export-tasks`) using Commander. +2. In the command's action handler, call a new method in storage.js (e.g. `exportToCSV(filename)`) that you will implement. +3. In storage.js, implement `exportToCSV(filename)`: +4. Retrieve all tasks (e.g. via `getAllTasks()`) +5. Convert tasks to CSV format (create headers, iterate over tasks to create rows) From 65ffecd38b48501ced2e4198c115d065891c25a2 Mon Sep 17 00:00:00 2001 From: ShaunvdBijl Date: Tue, 19 May 2026 10:58:50 +0200 Subject: [PATCH 3/8] Completed Exercise 3 --- .../task-manager/javascript/domainModel.md | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 use-cases/task-manager/javascript/domainModel.md diff --git a/use-cases/task-manager/javascript/domainModel.md b/use-cases/task-manager/javascript/domainModel.md new file mode 100644 index 00000000..be080f6c --- /dev/null +++ b/use-cases/task-manager/javascript/domainModel.md @@ -0,0 +1,190 @@ +# Exercise 3 +# Domain Model +## Extract domain model from codebase +### Core classes +- TaskStorage +- TaskManager +- TaskPriority (enum for task priority levels) +- TaskStatus (enum for task status) +- Task class + + +## Initial Understanding +### Simple Diagram +```mermaid +classDiagram + TaskStorage <|-- TaskManager + TaskManager o-- Task + Task : +id + Task : +title + Task : +description + Task : +priority + Task : +status + + flowchart LR + A[Task Class] --> B[TaskManager] + B --> C[TaskStorage] + C --> D(TaskPriority) + D --> E(TaskStatus) +``` +1. The main Class A is the starting point +2. Class A leads to B which is the TaskManager class that handles the logic of managing tasks +3. TaskManager interacts with C (TaskStorage) which is responsible for data persistence +4. Task C interacts with D and E which are enums for TaskPriority and TaskStatus respectively, defining the possible values for these properties in a task. + +## Prompts +### Prompt Used +I'd like you to act as a senior developer who deeply understands our codebase's domain model. I'm a junior developer trying to make sense of the business logic and domain concepts in this application +Here's what I've found in the codebase: +### Core classes +- TaskStorage +- TaskManager +- TaskPriority (enum for task priority levels) +- TaskStatus (enum for task status) +- Task class +### Simple Diagram +```mermaid +classDiagram + TaskStorage <|-- TaskManager + TaskManager o-- Task + Task : +id + Task : +title + Task : +description + Task : +priority + Task : +status + + flowchart LR + A[Task Class] --> B[TaskManager] + B --> C[TaskStorage] + C --> D(TaskPriority) + D --> E(TaskStatus) +``` +1. The main Class A is the starting point +2. Class A leads to B which is the TaskManager class that handles the logic of managing tasks +3. TaskManager interacts with C (TaskStorage) which is responsible for data persistence +4. Task C interacts with D and E which are enums for TaskPriority and TaskStatus respectively, defining the possible values for these properties in a task. +Could you, as a senior developer: +1. Validate my current understanding, correcting any misconceptions +2. Help me recognize the core domain concepts represented in this code +3. Explain the relationships between these entities in business terms, not just technical relationships +4. Clarify any domain-specific terminology or patterns I might be missing +5. Help me connect these models to actual user-facing features or business processes + +Then, please ask me 3-5 questions that would test my understanding of this domain model. These should be questions that make me think about the business logic, not just the code syntax. + +Finally, suggest a simple diagram I could sketch to visualize these relationships that would help solidify my understanding. + +### Prompt Response +1. Validation & Corrections ✅ / ⚠️ +What You Got Right: + +✅ Task is the core entity +✅ TaskManager orchestrates business logic +✅ TaskStorage handles persistence +✅ TaskPriority and TaskStatus are domain constraints +Critical Misconceptions to Fix: + +⚠️ Relationship Direction: Your diagram shows TaskStorage <|-- TaskManager (inheritance with TaskStorage being the parent). This is backwards. + +Reality: TaskManager depends on TaskStorage (composition, not inheritance) +Better notation: TaskManager --> TaskStorage (TaskManager uses TaskStorage) +Why it matters: This is about dependency flow, not class hierarchies. TaskManager is the high-level layer that delegates to TaskStorage. +⚠️ Missing Domain Concepts: You're missing critical attributes that represent business requirements: + +tags - How users organize/categorize tasks +dueDate, createdAt, updatedAt, completedAt - Temporal tracking (when things happen) +isOverdue() - Business logic (a task is "overdue" when it's past due date AND not done) +markAsDone() - Explicit state transition with side effects (sets completedAt automatically) +2. Core Domain Concepts (Business Language) +Let me translate the code into real business/product thinking: + +Code Concept Business Concept Why It Matters +Task A unit of work Everything revolves around this—it's what users care about +Priority (1-4) Urgency signal Helps teams decide what to tackle first +Status (TODO→IN_PROGRESS→REVIEW→DONE) Workflow state Models the journey of work through a team's process +Tags Flexible categorization Users can organize by project, team, feature, bug type, etc. +dueDate Deadline Creates accountability and urgency +isOverdue() Business rule A task is only "overdue" if it has a deadline AND hasn't been completed—this is critical logic +TaskManager Task coordinator The "brains" of the system—validates input, applies rules +TaskStorage Record keeper Like a filing cabinet—doesn't make decisions, just stores/retrieves +3. Relationships in Business Terms +Real example from the code: + +This shows layered architecture: + +Presentation Layer (cli.js): "What can the user do?" +Business Logic (app.js): "What are the rules?" +Data Layer (storage.js): "How do we remember?" +4. Domain-Specific Patterns You Should Know +Pattern 1: Value Objects (TaskPriority, TaskStatus) + +Pattern 2: Domain Logic Living in the Entity + +Pattern 3: State Transitions + +Pattern 4: Lazy vs. Active Properties + +5. Connecting to User-Facing Features +Here's how the domain model enables real features: + +User Goal Feature Domain Concepts Used +"Show me what to do today" list with filters Task.status, Task.dueDate +"I'm working on this" status in_progress TaskStatus enum, Task.update() +"This is urgent" priority 4 TaskPriority enum, Task.priority +"Mark it done when complete" status done Task.markAsDone(), Task.completedAt +"Show stats" stats TaskStorage aggregations (getAllTasks, getTasksByStatus) +"Which tasks are late?" list --overdue Task.isOverdue() business logic + +📊 Improved Domain Model Diagram +Here's a better visualization (corrects your relationship directions): + +Key differences from your diagram: + +TaskManager --> TaskStorage (dependency arrow, not inheritance) +Added temporal tracking (createdAt, updatedAt, completedAt) +Added tags and methods that represent business logic +TaskStatus and TaskPriority are shown as enumerations +Added method signatures so you see what each layer is responsible for +💡 Mental Model to Carry Forward +Think of it like a restaurant kitchen: + +Task = A plate that needs to be prepared (the product) +TaskManager = Head chef (makes decisions, ensures quality, follows recipes) +TaskStorage = Pantry (remembers ingredients, keeps organized records) +TaskStatus = Stage of the order (ordered → cooking → plating → served) +Priority = How urgently the order should be cooked +Understanding these business concepts makes it much easier to extend the system—you'll know where logic belongs and what constraints you need to maintain. + +### Prompt Questions +Q1: The "Overdue" Concept + +A task with dueDate = May 20 and status = DONE (completed on May 18). Is it overdue? Check the isOverdue() method in the code. Why does the business logic check this.status !== TaskStatus.DONE? + +Q2: Tags Design + +Why do you think tags are stored as an array tags: [] rather than a single string? What business scenarios would multiple tags enable? + +Q3: When Does completedAt Get Set? + +In the code, completedAt only gets set when markAsDone() is called. What happens if someone calls updateTaskStatus(taskId, TaskStatus.DONE) directly instead? Is this a bug or intentional? (Hint: look at the code in app.js) + +Q4: Data Flow for "Show Me Overdue Tasks" + +If a user runs list --overdue, trace the path through all three layers. Where does each layer add value? What would break if you removed TaskManager and called TaskStorage directly from CLI? + +Q5: Statistics Aggregation + +The getStatistics() method in TaskManager counts tasks by status and priority. Why is this logic in TaskManager rather than in TaskStorage? What business decisions does this enable at the management level? + +## Testing Knowledge +### Questions +**Q1** +No it is not overdue, the completed date is before due date and status is marked as done. The isOverdue() method checks if the status is not DONE because a task that is already completed cannot be overdue, regardless of its due date. +**Q2** +Tags are stored in a array to allow for multiple tags to be associated with a single task. This enables users to categorize and organize tasks in more flexible ways, such as by project, team, feature, or bug type. +**Q3** +This is likely intentional. The markAsDone() method not only sets the status to DONE but also records the completedAt timestamp. If someone calls updateTaskStatus(taskId, TaskStatus.DONE) directly, it would change the status but would not set the completedAt timestamp, which could lead to incomplete data about when tasks were actually completed. +**Q4** +When a user runs list --overdue, the CLI layer (cli.js) would parse the command and recognize the --overdue flag. It would then call a method in TaskManager (app.js) that retrieves all tasks from TaskStorage and applies the isOverdue() logic to filter out only those tasks that are overdue. If you removed TaskManager and called TaskStorage directly from CLI, you would lose the business logic encapsulated in TaskManager, such as filtering for overdue tasks, and you would have to implement that logic in the CLI layer, which is not ideal for separation of concerns. +**Q5** +The getStatistics() method is in TaskManager rather than TaskStorage because it involves business logic that aggregates and interprets the raw data of tasks. TaskStorage's responsibility is to store and retrieve data, while TaskManager's role is to apply business rules and provide meaningful insights. By keeping this logic in TaskManager, it allows for more complex calculations and decision-making based on the task data, which can inform management decisions about resource allocation, project progress, and areas that may need attention. \ No newline at end of file From 3c308ddddae0f9d401c7466a2e5f500de5d017fc Mon Sep 17 00:00:00 2001 From: ShaunvdBijl Date: Thu, 21 May 2026 10:30:22 +0200 Subject: [PATCH 4/8] Knowing where to Start --- .../javascript/finalDiscussion.md | 11 + .../task-manager/javascript/practical.md | 289 ++++++++++++++++++ .../task-manager/javascript/submission.md | 28 ++ 3 files changed, 328 insertions(+) create mode 100644 use-cases/task-manager/javascript/finalDiscussion.md create mode 100644 use-cases/task-manager/javascript/practical.md create mode 100644 use-cases/task-manager/javascript/submission.md diff --git a/use-cases/task-manager/javascript/finalDiscussion.md b/use-cases/task-manager/javascript/finalDiscussion.md new file mode 100644 index 00000000..0c959968 --- /dev/null +++ b/use-cases/task-manager/javascript/finalDiscussion.md @@ -0,0 +1,11 @@ +# Final Discussion +## Approach to understanding codebase +I found it quite fun to read through a code base and gaining a my own perspective on what it is about adn the using the prompts I leant to see what I missed and what I got tight +## Challenges +My main problem was running out of prompts as my chatbot had limited propmts. I created a GitHub with the email I was given for the course so I had a fresh account for prompts +## Most useful prompts +Prompt 1 was the most helpful as I could properly understand the logic of the project +## For future +I would use the prompt to identify what the local imported libraries are for and what they do as I had a hard time understanding the commander library and how it worked with the cli.js file. +## Additional tools +I would use a code editor with better search capabilities and a better terminal to run the project and test my changes. I also would have used a better chatbot with more prompts to ask about the project and my changes. \ No newline at end of file diff --git a/use-cases/task-manager/javascript/practical.md b/use-cases/task-manager/javascript/practical.md new file mode 100644 index 00000000..7ed1c70c --- /dev/null +++ b/use-cases/task-manager/javascript/practical.md @@ -0,0 +1,289 @@ +# Exercise 4: Practical Implementation Planning +## Scenario: Automatic Abandoned Task Rule + +**Business Rule:** "Tasks that are overdue for more than 7 days should be automatically marked as abandoned unless they are marked as high priority." + +--- + +## Planning + +### 1. Files That Need Modification + +#### **models.js** (HIGH PRIORITY) +- **Purpose:** Add new domain concept for "abandoned" state +- **Changes:** + - Add new `TaskStatus.ABANDONED = 'abandoned'` constant + - OR add `abandoned: boolean` property to Task class (decision needed) + - Add helper method `canBeMarkedAsAbandoned()` to Task class that checks: + - `isOverdue()` returns true (already exists) + - Days overdue > 7 + - `priority !== TaskPriority.HIGH` (protects high-priority tasks) + +#### **storage.js** (HIGH PRIORITY) +- **Purpose:** Retrieve tasks matching the abandonment criteria +- **Changes:** + - Add `getTasksEligibleForAbandonment()` method that: + - Returns all tasks where `isOverdue() === true` + - Filters to those with dueDate older than 7 days + - Excludes high-priority tasks + - Excludes already-done or abandoned tasks + - Add `markTaskAsAbandoned(taskId)` method to transition task to abandoned state + +#### **app.js** (HIGH PRIORITY) +- **Purpose:** Implement business logic and orchestrate the abandonment process +- **Changes:** + - Add `abandonOverdueHighRiskTasks()` public method that: + - Calls `storage.getTasksEligibleForAbandonment()` + - Iterates through results and calls `updateTaskStatus(taskId, TaskStatus.ABANDONED)` + - Returns count of affected tasks for logging + - Add `updateTaskStatus()` wrapper to handle ABANDONED state transitions + - Consider: Should we log which tasks were abandoned? + +#### **cli.js** (MEDIUM PRIORITY) +- **Purpose:** Expose the abandonment process to users +- **Changes Option A (On-Demand):** + - Add new command: `program.command('cleanup-abandoned')` + - Calls `taskManager.abandonOverdueHighRiskTasks()` + - Displays count of tasks marked as abandoned + +- **Changes Option B (Automatic Check):** + - Call `taskManager.abandonOverdueHighRiskTasks()` on startup + - Call in `listTasks()` before returning results (keeps data fresh) + +--- + +### 2. Outline of Implementation Changes + +#### **Step 1: Domain Model Extension (models.js)** +```javascript +// Add to TaskStatus enumeration +const TaskStatus = { + TODO: 'todo', + IN_PROGRESS: 'in_progress', + REVIEW: 'review', + DONE: 'done', + ABANDONED: 'abandoned' // NEW +}; + +// Add method to Task class +canBeMarkedAsAbandoned() { + // Rule: overdue + not high priority + if (!this.isOverdue()) return false; // Not even overdue yet + if (this.priority === TaskPriority.HIGH) return false; // Protected by priority + + // Check if more than 7 days overdue + const daysOverdue = Math.floor( + (new Date() - this.dueDate) / (1000 * 60 * 60 * 24) + ); + return daysOverdue > 7; +} +``` + +#### **Step 2: Data Layer (storage.js)** +```javascript +// Add to TaskStorage class +getTasksEligibleForAbandonment() { + return Object.values(this.tasks).filter(task => + task.canBeMarkedAsAbandoned() + ); +} + +// Enhanced updateTask to handle abandoned state +updateTask(taskId, updates) { + const task = this.getTask(taskId); + if (task) { + if (updates.status === TaskStatus.ABANDONED) { + task.abandonedAt = new Date(); // Track when abandoned + } + task.update(updates); + this.save(); + return true; + } + return false; +} +``` + +#### **Step 3: Business Logic (app.js)** +```javascript +// Add to TaskManager class +abandonOverdueHighRiskTasks() { + const tasksToAbandon = this.storage.getTasksEligibleForAbandonment(); + const count = tasksToAbandon.length; + + tasksToAbandon.forEach(task => { + this.updateTaskStatus(task.id, TaskStatus.ABANDONED); + }); + + if (count > 0) { + console.log(`[SYSTEM] Marked ${count} task(s) as abandoned`); + } + + return count; +} +``` + +#### **Step 4: Presentation Layer (cli.js)** +```javascript +// Option A: On-demand command +program + .command('cleanup') + .description('Mark overdue tasks as abandoned (7+ days overdue, not high priority)') + .action(() => { + const count = taskManager.abandonOverdueHighRiskTasks(); + console.log(`Cleanup complete: ${count} task(s) marked as abandoned.`); + }); + +// Option B: Call on startup +// In the taskManager initialization or at the start of main execution: +taskManager.abandonOverdueHighRiskTasks(); +``` + +--- + +### 3. Questions for Your Team + +#### **Architectural Decisions** + +1. **When should this rule execute?** + - [ ] On every startup (automatic hygiene) + - [ ] On-demand via CLI command (user-triggered) + - [ ] Both (check on startup AND provide manual command) + - [ ] Periodically in the background (would need a scheduler) + +2. **Should "abandoned" be a new status or a flag?** + - Current approach: New status in TaskStatus enum + - Alternative: Keep status as-is, add `abandoned: boolean` property + - Why this matters: Abandoned affects filtering, reporting, and UI display + +3. **What about reverting abandoned tasks?** + - Can users manually change status back from ABANDONED → TODO? + - Should there be an "unabandoned" reason/comment? + - How do we prevent users from just re-abandoning the same task infinitely? + +4. **Should high-priority threshold be different?** + - Current rule: High priority tasks NEVER auto-abandon + - Alternative: High priority gets extended grace period (14 days instead of 7) + - What defines "high priority" in your business domain? + +5. **How does this affect statistics and reporting?** + - Should abandoned tasks be excluded from "overdue" counts? + - Should we have an "abandoned" category in stats? + - Impact on KPIs and dashboards? + +6. **What's the user notification strategy?** + - Should we email/notify users when their task is about to be abandoned? + - Should there be a "7-day warning" before auto-abandon? + - Or is this silent system maintenance? + +7. **Audit trail requirements:** + - Should we track WHO marked a task abandoned (system vs user)? + - Do we need to log the reason? + - Do we need `abandonedAt` timestamp (I assumed yes)? + +--- + +## Reflection + +### How Did the AI Prompts Help Understanding? + +✅ **Understanding Architectural Layers:** +- The three-layer model (CLI → Business Logic → Storage) made it immediately clear where each piece belongs +- Without this, I might have put all logic in cli.js or scattered across files +- Recognizing that each layer has a specific responsibility made the decision obvious + +✅ **Domain Model Thinking:** +- Framing this as a "business rule" rather than "code feature" helped identify that this is a new domain concept +- Understanding TaskStatus as a value object (restricted set) made it natural to add ABANDONED status +- Knowing that Task has behavior (isOverdue, markAsDone) made me think about adding a `canBeMarkedAsAbandoned()` method to the Task itself + +✅ **Understanding Dependencies:** +- Seeing that TaskManager depends on TaskStorage (not the other way) showed me that TaskManager should orchestrate the abandonment, not TaskStorage +- This prevents the storage layer from making business decisions + +✅ **Connection to Features:** +- Understanding how `listTasks()` works helped me see that I could hook abandonment logic there OR in a separate command +- Knowing existing commands helped me design the new CLI command consistently + +--- + +### Aspects Still Uncertain + +❓ **Uncertainty 1: State Transition Design** +- Should abandoned be a terminal state (like DONE)? +- Or can abandoned tasks transition back to TODO if user revives them? +- The current code seems to assume tasks flow: TODO → IN_PROGRESS → REVIEW → DONE +- Is abandonment a parallel state or part of this flow? + +❓ **Uncertainty 2: When to Check** +- On-demand vs automatic affects system design significantly +- If automatic (on startup), we need to ensure it runs BEFORE CLI commands execute +- If automatic (periodic), we need scheduler infrastructure I haven't seen +- The code doesn't show any scheduler or background job system + +❓ **Uncertainty 3: Data Migration** +- Do existing tasks that are already 8+ days overdue get retroactively abandoned? +- Or only new tasks going forward? +- This affects the implementation (might need a migration script) + +❓ **Uncertainty 4: Testing Time-Based Logic** +- How do we test "more than 7 days" without manipulating system time? +- Should dueDate be injectable for testing? +- Should we have a `isOverdueByDays(days)` method to test the boundary? + +❓ **Uncertainty 5: High Priority Definition** +- Is "high priority" always TaskPriority.HIGH? +- Or should we make it configurable (e.g., HIGH and URGENT never auto-abandon)? +- The business might want different rules later + +--- + +### Next Steps to Deepen Understanding + +**Step 1: Code Exploration (Review Complete Methods)** +- [ ] Read entire app.js to see all methods and how they use storage +- [ ] Read entire storage.js to understand all data retrieval patterns +- [ ] Look for any existing time-based or calculated methods (best practices) + +**Step 2: Pattern Matching** +- [ ] Find similar business rules in the code (e.g., isOverdue implementation) +- [ ] See how existing statuses are handled in updates +- [ ] Check how statistics method aggregates by status (template for our changes) + +**Step 3: Architecture Deep Dive** +- [ ] Trace a complete user command from CLI through all layers (e.g., `delete `) +- [ ] Understand the exact flow of data and state changes +- [ ] Identify any hooks or patterns for system-level operations + +**Step 4: Integration Points** +- [ ] Check if there's a main entry point or initialization pattern +- [ ] See if TaskManager is instantiated once or per command +- [ ] Understand when save() is called and how persistence works + +**Step 5: Ask Clarifying Questions** +- [ ] Ask team: Is this rule immediate need or future feature? +- [ ] Ask team: What triggers this rule in real usage? +- [ ] Ask team: Are there other similar auto-maintenance rules planned? + +**Step 6: Implementation Strategy** +- [ ] Start with models.js (add ABANDONED status and helper method) +- [ ] Add storage.js method to query eligible tasks +- [ ] Add app.js orchestration method +- [ ] Test in isolation with mock data +- [ ] Add CLI integration last (after business logic works) + +--- + +## Key Insights from This Exercise + +**1. Business Logic Belongs in the Domain:** +The `canBeMarkedAsAbandoned()` method belongs on the Task itself, not scattered in TaskManager. This keeps the rule close to the data it validates. + +**2. Layers Enable Flexibility:** +Because we have separate layers, we can implement the abandonment check as on-demand CLI command OR automatic startup cleanup with minimal changes—just where the method is called. + +**3. New Status = New Behavior:** +Adding ABANDONED to TaskStatus isn't just a naming thing—it affects filtering, sorting, reporting, and user experience. It's a first-class domain concept. + +**4. Questions Drive Design:** +The unanswered questions above aren't blockers—they're design opportunities. Each answer shapes the implementation. This is why senior developers ask "why" before coding. + diff --git a/use-cases/task-manager/javascript/submission.md b/use-cases/task-manager/javascript/submission.md new file mode 100644 index 00000000..8e6494f4 --- /dev/null +++ b/use-cases/task-manager/javascript/submission.md @@ -0,0 +1,28 @@ +# Final Submission + +## Initial Understanding vs Final Understanding +My initial understanding of the project was pretty accurate. I correctly identified the main files and their purposes. I also correctly identified the main components and technologies used in the project. My initial understanding of the project structure was also correct. +My final understanding of the project is that it is a simple task manager application that allows users to create, read, update, and delete tasks. The project uses a command line interface to interact with the user and stores data in a file. The main components of the project are the CLI tools and file pathing. The project uses the commander library for creating the CLI and uuidv4 for generating unique IDs for tasks. + +## Most value gained from each prompt +### Prompt 1 +This prompt was the most helpful as it helped me understand the overall structure of the project and the main components. It also helped me understand the technologies used in the project and how they fit together. +### Prompt 2 +This prompt was helpful in understanding where to find the code for the CSV export feature. It helped me identify which files and directories were most likely to contain the implementation for this feature and suggested specific search terms to use. It also helped me understand how the feature might be located in different areas of the codebase and recommended a step-by-step investigation process to understand the complete feature flow. +### Prompt 3 +This prompt was helpful in understanding the challenges I faced while exploring the codebase and how I overcame them. It also helped me reflect on the most useful prompts and what I would do differently in the future when exploring a new codebase. It also helped me identify additional tools that could have been helpful in my exploration. + +## Implementing new business rules +1. I would first read through the existing code to understand how the current business rules are implemented and where they are located in the codebase. +2. I would then identify where the new business rules need to be implemented and how they fit into the existing code structure. +3. I would write the code for the new business rules, making sure to follow the existing coding style and conventions used in the project. +4. I would then test the new business rules to ensure they work as expected and do not break any existing functionality. +5. Finally, I would document the new business rules and any changes made to the codebase to ensure that other developers can understand the changes and how to use the new features. + +## New startegies for unfimiliar codebases +1. Start with a high-level overview: Read the README and any documentation to get a general understanding of the project and its purpose. +2. Identify the main components: Look for the main files and directories in the codebase and try to understand their roles and how they interact with each other. +3. Use search strategically: Instead of searching for specific features, search for architectural patterns and keywords that can help you understand the structure of the codebase. +4. Trace data flow: Follow the flow of data through the codebase to understand how different components interact and where key functionalities are implemented. +5. Ask questions: Don't be afraid to ask your team for clarification on parts of the codebase that you don't understand. This can help you gain insights and learn from more experienced developers. +6. Use tools: Utilize code editors with good search capabilities, debuggers, and other tools that can help you navigate and understand the codebase more effectively. \ No newline at end of file From 8ee192ceccaf0b57bc3f37b439907ae970363970 Mon Sep 17 00:00:00 2001 From: ShaunvdBijl Date: Thu, 21 May 2026 11:20:27 +0200 Subject: [PATCH 5/8] CodeBase Exploration --- use-cases/task-manager/javascript/dataFlow.md | 265 ++++++++++++++++++ .../javascript/deepenUnderstanding.md | 138 +++++++++ .../task-manager/javascript/reflextion.md | 32 +++ .../javascript/specificFeature.md | 193 +++++++++++++ 4 files changed, 628 insertions(+) create mode 100644 use-cases/task-manager/javascript/dataFlow.md create mode 100644 use-cases/task-manager/javascript/deepenUnderstanding.md create mode 100644 use-cases/task-manager/javascript/reflextion.md create mode 100644 use-cases/task-manager/javascript/specificFeature.md diff --git a/use-cases/task-manager/javascript/dataFlow.md b/use-cases/task-manager/javascript/dataFlow.md new file mode 100644 index 00000000..fe0fb01a --- /dev/null +++ b/use-cases/task-manager/javascript/dataFlow.md @@ -0,0 +1,265 @@ +# Exercise 3: Mapping Data Flow +## Entry point and components involved when marking task as complete +Entry point is at app.js or cli.js +1. cli.js receives the command to mark a task as complete (e.g. `task-manager complete `) +2. cli.js parses the command and extracts the task ID +3. cli.js calls a method in app.js (e.g. `taskManager.markTaskAsDone(taskId)`) +4. app.js receives the task ID and calls a method in models.js to update the task status (e.g. `task.markAsDone()`) +5. models.js updates the task's status to "done" and returns the updated task object + +## Code that handle state changes when marking a task as complete +In app.js: +```javascriptupdateTaskStatus(taskId, newStatusValue) { + if (newStatusValue === 'done') { + return this.storage.updateTask(taskId, { status: newStatusValue }); + } else if (newStatusValue === 'pending') { + return this.storage.updateTask(taskId, { status: newStatusValue }); + } else { + console.error("Invalid status value. Use 'done' or 'pending'."); + return false; + } + } +``` +In models.js: +```javascriptmarkAsDone() { + if (this.status === 'done') { + console.warn("Task is already marked as done."); + return false; + } else if (this.status === 'pending') { + this.status = 'done'; + return true; + } else { + console.error("Invalid task status. Cannot mark as done."); + return false; + } + } +markAsPending() { + if (this.status === 'pending') { + console.warn("Task is already marked as pending."); + return false; + } else if (this.status === 'done') { + this.status = 'pending'; + return true; + } else { + console.error("Invalid task status. Cannot mark as pending."); + return false; + } + } +``` +## Prompt +### Prompt Used +I need help understanding how data flows through our JavaScript, CLI, Node.js application, specifically for the marked a s complete functionality. + +What I know: +The feature starts at cli.js +It seems to involve these files +cli.js -> app.js -> models.js +Here are the key code snipets: + +In app.js: +```javascriptupdateTaskStatus(taskId, newStatusValue) { + if (newStatusValue === 'done') { + return this.storage.updateTask(taskId, { status: newStatusValue }); + } else if (newStatusValue === 'pending') { + return this.storage.updateTask(taskId, { status: newStatusValue }); + } else { + console.error("Invalid status value. Use 'done' or 'pending'."); + return false; + } + } +``` +In models.js: +```javascriptmarkAsDone() { + if (this.status === 'done') { + console.warn("Task is already marked as done."); + return false; + } else if (this.status === 'pending') { + this.status = 'done'; + return true; + } else { + console.error("Invalid task status. Cannot mark as done."); + return false; + } + } +markAsPending() { + if (this.status === 'pending') { + console.warn("Task is already marked as pending."); + return false; + } else if (this.status === 'done') { + this.status = 'pending'; + return true; + } else { + console.error("Invalid task status. Cannot mark as pending."); + return false; + } + } +``` +What I'm trying to understand: 1. What is the complete path data takes from input to output? 2. How and where is state managed throughout this process? 3. What transformations happen to the data at each step? 4. Where are potential points of failure in this data flow? 5. How does the application handle edge cases or errors? +Could you help me create a clear mental model of: 1. The complete data flow diagram 2. Where and how state changes occur 3. How I might debug issues in this flow 4. What I should consider if I need to modify this data flow in the future Please include any relevant descriptions, design patterns or principles that would help me understand the existing code decisions. +### Prompt Returned +# Mark as complete — data flow analysis + +## Critical finding: disconnected model methods + +Before anything else, there is a **significant discrepancy** in the code you shared. + +The `updateTaskStatus` in `app.js` handles status changes with a direct `storage.updateTask()` call — but it **never calls `markAsDone()` or `markAsPending()`** from `models.js`. Those model methods exist but are bypassed entirely. This means all the guard logic in `models.js` (the "already done" warnings, invalid state checks) has no effect at runtime. + +This is either a bug or two different versions of the codebase. Worth confirming which `app.js` is actually running. + +--- + +## 1. Complete data flow: input to output + +``` +User types: done + | + v + cli.js + Parses command, extracts taskId and hardcodes 'done' as the status string + | + | taskId, 'done' + v + app.js — updateTaskStatus(taskId, newStatusValue) + String comparison: is newStatusValue === 'done' or 'pending'? + | + |--- 'done' ---> storage.updateTask(taskId, { status: 'done' }) + |--- 'pending' ---> storage.updateTask(taskId, { status: 'pending' }) + |--- anything else -> console.error(), return false + | + v + storage + Persists { status: 'done' } to disk/memory, returns true/false + | + v + cli.js + Prints success or failure message to terminal +``` + +--- + +## 2. State management + +State lives in **two places** and they can diverge: + +| Layer | What it holds | When it updates | +|---|---|---| +| `storage` | The persisted task record | On every `updateTask()` call | +| `Task` object (models.js) | In-memory instance with `this.status` | Only when `markAsDone()` / `markAsPending()` is called — which currently never happens via `app.js` | + +Because `app.js` calls `storage.updateTask()` directly rather than going through the model, the `Task` object's in-memory state is **never updated** during a status change. If anything later reads a cached `Task` instance rather than fetching fresh from storage, it will see stale data. + +--- + +## 3. Data transformations at each step + +| Step | Transformation | +|---|---| +| cli.js input | Raw string from terminal → `taskId` (string) and `'done'` (hardcoded string) | +| app.js | No transformation — strings passed straight to storage | +| storage.updateTask | Merges `{ status: 'done' }` into the existing task record | +| Return value | `true` (success) or `false` (not found / error) propagates back up | + +Notably, **there is no type coercion, validation, or transformation** of the status value in the current `app.js`. It relies entirely on the string matching `'done'` or `'pending'` exactly. + +--- + +## 4. Failure points + +### F1 — No input sanitisation in cli.js +The status string passed to `updateTaskStatus` comes directly from CLI parsing. If the command is constructed differently (e.g. passing `'Done'` with a capital D), the string comparison fails silently and returns `false`. + +### F2 — Plain string comparison instead of enum +`app.js` compares against raw strings `'done'` and `'pending'` rather than using a `TaskStatus` enum. This means a typo anywhere in the codebase (e.g. `'Done'`, `'complete'`, `'DONE'`) creates a silent failure — no exception, just `false`. + +### F3 — `markAsDone()` / `markAsPending()` are dead code +The guard logic in `models.js` — including "task already done" warnings and invalid state checks — is never reached. A task can be marked done twice with no warning. + +### F4 — No task existence check before update +`app.js` calls `storage.updateTask()` directly without first checking if the task exists. Whether this is safe depends entirely on `storage.updateTask()`'s implementation — if it silently does nothing on a missing ID, the caller gets a misleading `false` with no explanation. + +### F5 — Return value is a bare boolean +`cli.js` receives `true` or `false` with no error detail. When something fails, the user sees a generic failure message with no indication of *why* — invalid ID, invalid status, storage error, or something else entirely. + +--- + +## 5. Debugging guide + +### Symptom: command runs but status doesn't change +- Check the exact string being passed from cli.js — log `newStatusValue` at the top of `updateTaskStatus` +- Confirm `storage.updateTask()` is returning `true` +- Check whether the task ID exists in storage before the call + +### Symptom: no error thrown but `false` returned +- Add a temporary `console.log` at each branch in `updateTaskStatus` to identify which path executed +- Verify the status string matches `'done'` or `'pending'` exactly (case-sensitive) + +### Symptom: task shows old status after update +- Check whether anything is reading from a cached `Task` instance rather than fetching fresh from storage +- Remember: `Task.status` in memory is never updated by the current `app.js` flow + +--- + +## 6. Design patterns at play + +**Layered architecture** — cli.js handles I/O, app.js handles business logic, models.js defines the domain, storage handles persistence. The intent is clean separation of concerns. + +**Anemic domain model (current state)** — In the current implementation, the `Task` model is effectively a data container. Its methods (`markAsDone`, `markAsPending`) exist but have no callers, so all logic has leaked into `app.js`. This is a common anti-pattern that makes the model harder to test and reason about independently. + +**What it should look like** — The richer version (seen in the earlier `app.js` snippet) fetches the task object, calls `task.markAsDone()`, and then calls `storage.save()`. That pattern keeps domain logic in the model and uses storage only for persistence — the intended architecture. + +--- + +## 7. What to consider before modifying this flow + +1. **Decide on the source of truth for status** — should it be the `Task` object, or the storage record? The current code treats them as independent, which causes drift. +2. **Replace string literals with a `TaskStatus` enum** — this eliminates an entire class of typo bugs and makes refactoring safer. +3. **Reconnect `markAsDone()` / `markAsPending()`** — route through the model methods so their guard logic actually runs. +4. **Add a task existence check** — `updateTaskStatus` should verify the task exists before attempting an update and return a meaningful error if not. +5. **Consider richer return types** — returning `{ success: boolean, error?: string }` instead of a bare boolean gives the CLI layer enough information to show the user a useful message. + +## Task Completion Data Flow Journal + +### 1. Data Flow Diagram +- `cli.js` receives user command +- parses `taskId` +- calls `app.js` to update status +- `app.js` receives `taskId` and target status +- applies business logic +- forwards update to persistence +- `models.js` contains task domain logic +- changes task object state from pending to done +- `storage` persists the updated task record +- returns success/failure to `app.js` +- `cli.js` displays result to user + +### 2. State Changes During Task Completion +- Initial state: task exists with status = `pending` +- User action: CLI command triggers request +- Business logic: `app.js` determines the update path +- Model state change: `models.js` updates `Task.status = 'done'` +- Persistence: `storage.updateTask(taskId, { status: 'done' })` +- Final state: persisted task record now reflects done + +### 3. Potential Points of Failure +- CLI parsing error + - wrong task ID extracted + - malformed command input +- Application logic mismatch + - `app.js` bypasses model guards + - invalid status string passed +- Domain validation failure + - model rejects already-done or invalid status +- Persistence failure + - task ID not found + - storage write fails +- Poor error visibility + - only `true`/`false` returned + - no detailed failure reason surfaced + +### 4. How the Application Persists These Changes +- Persistence occurs in the storage layer +- `app.js` delegates final write to `storage.updateTask(...)` +- The storage layer stores the updated task record +- This makes storage the source of truth for task status +- In-memory task state must stay synced with storage to avoid stale state \ No newline at end of file diff --git a/use-cases/task-manager/javascript/deepenUnderstanding.md b/use-cases/task-manager/javascript/deepenUnderstanding.md new file mode 100644 index 00000000..ab8a40d1 --- /dev/null +++ b/use-cases/task-manager/javascript/deepenUnderstanding.md @@ -0,0 +1,138 @@ +# Exercise 2: Deepen Understanding of the Codebase +## My prompt +I'd like your help in deepening my understanding of a codebase by you acting as my senior development pair programmer. First, I'll share my current understanding of the code I'm exploring. Please acknowledge what parts I've understood correctly, then guide me with thoughtful questions rather than direct explanations. ## My Current Understanding + +I'm exploring the priority feature. From what I can tell, each task is assigned an enum priority from: low, medium, high, and urgent. + +The files involved are: `app.js`, `models.js`, `cli.js` + +## What I'd Like From You + +1. Acknowledge what parts of my understanding seem accurate +2. Ask me 3-5 thoughtful questions that will help me discover insights about: + - What specific parts of the code actually do + - How data moves through these functions + - What happens in different scenarios (like error cases) + - Why certain implementation choices were made +3. For some questions, ask me to share specific code snippets that would help answer the question +4. End with a practical question about how I might use or modify this code + +**Please don't give me direct explanations - help me discover the answers through guided questioning.** +## Prompt Response + +Your understanding is solid as a starting point. You've correctly identified that priority is likely enum-based with those four levels, and that the feature spans all three files — which is the right mental model for how this codebase separates concerns (CLI handles input, app.js orchestrates, models.js holds the data shape). + +A couple of things you've got right that are worth building on: the enum pattern is a deliberate choice over plain strings or numbers, and the fact that createTask in app.js accepts a priorityValue parameter (defaulting to 2) already hints at how priorities map internally. + +Here are some questions to guide you deeper: + +1. You mentioned the enum has four levels: low, medium, high, and urgent. In createTask, the default priority is priorityValue = 2. Without looking it up — what do you think 2 maps to in the enum, and why might the developer have chosen that as the default? What does that tell you about how the enum is structured under the hood? + +2. Think about the journey of a priority value from the moment a user types a command in the terminal to when it's saved on a task. Can you trace that path through cli.js → app.js → models.js and share the relevant snippets from each file? Specifically: is the value ever transformed or validated along the way, or does it pass through as-is? + +3. What happens if a user passes an invalid priority — say, the number 99 or the string "critical"? Can you find where (if anywhere) that case is handled, and share that code? If you can't find any guard, what does that tell you about a potential gap in the implementation? + +4. Look at how priority is used after a task is created — for example, when tasks are listed or sorted. Does the codebase ever compare two priorities against each other? If so, share that snippet. What does the comparison method tell you about why an enum (or numeric mapping) was chosen over plain strings like "high" or "low"? + +5. Finally, a practical one: if you were asked to add a fifth priority level called "critical" that sits above "urgent", what's the minimum set of changes you'd need to make across these three files? Before touching any code, where would you start and why? + +## My answers + +**Question 1:** 2 refers to medium, the enum runs from 1+ in increments of 1 + +**Question 2:** In cli.js: + +```javascript +const prioritySymbol = { + [TaskPriority.LOW]: '!', + [TaskPriority.MEDIUM]: '!!', + [TaskPriority.HIGH]: '!!!', + [TaskPriority.URGENT]: '!!!!' +}; + +program + .command('list') + .description('List all tasks') + .option('-s, --status ', 'Filter by status') + .option('-p, --priority ', 'Filter by priority') + .option('-o, --overdue', 'Show only overdue tasks') + .action((options) => { + const tasks = taskManager.listTasks(options.status, options.priority, options.overdue); + if (tasks.length > 0) { + tasks.forEach(task => { + console.log(formatTask(task)); + console.log('-'.repeat(50)); + }); + } else { + console.log('No tasks found matching the criteria.'); + } + }); + +program + .command('priority ') + .description('Update task priority') + .action((taskId, priority) => { + if (taskManager.updateTaskPriority(taskId, priority)) { + console.log(`Updated task priority to ${priority}`); + } else { + console.log('Failed to update task priority. Task not found.'); + } + }); +``` + +In app.js: + +```javascript +updateTaskPriority(taskId, newPriorityValue) { + return this.storage.updateTask(taskId, { priority: parseInt(newPriorityValue) }); +} +``` + +This tells me that a value is initialized in cli.js and in app.js the priority can change. It should reassign the value to 2 as it is the default. + +**Question 4:** I could not find any comparisons + +**Question 5:** I would look through models.js to see how the enum works there, then how the enum value is called in app.js and cli.js to ensure it does not break the logic. This leads to 3 file changes and about 7 code edits. + +## Prompt feedback + +Great digging — you've uncovered some genuinely important things here. Your enum mapping is spot on (medium = 2 as the default makes intuitive sense). And your instinct for the "critical" addition — start in models.js, then work outward — is exactly the right approach senior devs call following the dependency direction. + +A couple of things worth gently challenging though, before moving on: + +**On question 3** — you said an invalid value like 99 would be reassigned to 2 (the default). But look again at updateTaskPriority in app.js: + +```javascript +updateTaskPriority(taskId, newPriorityValue) { + return this.storage.updateTask(taskId, { priority: parseInt(newPriorityValue) }); +} +``` + +The default 2 only applies at creation time in createTask. This function receives whatever the CLI passes and calls parseInt() on it directly. So — what do you think actually happens to that 99 here? Is there anything stopping it from being saved to the task? + +**On question 4** — you couldn't find priority comparisons, which is itself a meaningful finding. Think about this: the CLI has a --priority filter option that gets passed into taskManager.listTasks(). Can you find listTasks in app.js and share that snippet? I'm curious whether filtering tasks by priority involves any comparison logic — and if so, what type of comparison it uses. + +**And a new thread to pull on:** Look at prioritySymbol in cli.js again: + +```javascript +const prioritySymbol = { + [TaskPriority.LOW]: '!', + [TaskPriority.MEDIUM]: '!!', + [TaskPriority.HIGH]: '!!!', + [TaskPriority.URGENT]: '!!!!' +}; +``` + +This is used for display purposes. What happens to this object if someone passes 99 as a priority and it gets saved successfully? What would the user see when they list their tasks? + +## My remarks +## Initial Understanding vs discovered +My initial understanding of the priority feature was that it was enum-based with four levels: low, medium, high, and urgent. I also understood that the feature spanned across cli.js, app.js, and models.js. +My deeper exploration revealed that the default priority is set to medium (2) in createTask, and that the updateTaskPriority function in app.js does not have any validation for invalid priority values. This means that if a user passes an invalid priority like 99, it would be saved to the task without any guard against it. Additionally, I found that there are no comparisons for priority levels in the codebase, which suggests that the enum structure is primarily for display purposes rather than for logic-based comparisons. +## Key insights gained +1. The default priority is set to medium (2) in createTask, which indicates that the enum is structured with low = 1, medium = 2, high = 3, and urgent = 4. +2. The updateTaskPriority function does not validate the new priority value, which means that invalid values can be saved to tasks without any error handling. +3. The prioritySymbol object in cli.js is used for display purposes, and if an invalid priority like 99 is saved, it would not have a corresponding symbol and could lead to unexpected display output when listing tasks. +## Misconseptions that were clarified +1. I initially thought that the default priority would be applied in updateTaskPriority if an invalid value was passed, but I learned that the default only applies during task creation and that updateTaskPriority does not have any validation for new priority values. +2. I assumed that there might be comparisons for priority levels in the codebase, but I found that there are none, which clarified that the enum is primarily for display rather than for logic-based comparisons. \ No newline at end of file diff --git a/use-cases/task-manager/javascript/reflextion.md b/use-cases/task-manager/javascript/reflextion.md new file mode 100644 index 00000000..0038e6e8 --- /dev/null +++ b/use-cases/task-manager/javascript/reflextion.md @@ -0,0 +1,32 @@ +# Reflection + +## Review and Consolidated Understanding +I reviewed the journal and the code analysis related to task creation, prioritization, and completion. The application uses a CLI layer for user input, an app layer for business logic, a model layer for task domain behavior, and a storage layer for persistence. This separation helps the app handle commands, validate or transform data, apply domain rules, and persist changes independently. + +## 3–5 Minute Presentation + +### High-level Application Architecture +- `cli.js`: receives terminal commands and translates user input into app calls. +- `app.js`: orchestrates core features, validates inputs, and coordinates models and storage. +- `models.js`: defines the `Task` behavior and encapsulates domain logic like marking done or pending. +- `storage`: persists tasks and serves as the source of truth for task records. + +### How the Three Key Features Work +- **Task creation** + - `cli.js` parses a create command. + - `app.js` validates fields like due date and constructs a `Task`. + - `storage.addTask(task)` saves the task and returns its ID. +- **Prioritization** + - `cli.js` accepts a priority update command. + - `app.js` forwards the priority change to storage. + - The priority is stored as a numeric enum-like value, and CLI display maps it to symbols. +- **Completion** + - `cli.js` runs the complete command with a task ID. + - `app.js` either updates storage directly or uses `Task.markAsDone()` for the done transition. + - The status change is persisted through `storage.updateTask()` or `storage.save()`. + +### One Interesting Design Pattern or Approach +The most interesting pattern is the layered separation between CLI, app logic, domain model, and persistence. This structure enables the model to own task behavior while the storage layer remains responsible for saving state, even though some paths currently bypass the model. + +### What I Found Most Challenging +I found it challenging to determine whether the model methods were actually used during completion because the code shows both a direct storage update path and a model-driven path. The prompts helped by highlighting the discrepancy between `updateTaskStatus()` and the `markAsDone()` / `markAsPending()` methods, making it clearer that there may be an architectural mismatch or dead code. diff --git a/use-cases/task-manager/javascript/specificFeature.md b/use-cases/task-manager/javascript/specificFeature.md new file mode 100644 index 00000000..14e7f88a --- /dev/null +++ b/use-cases/task-manager/javascript/specificFeature.md @@ -0,0 +1,193 @@ +# Exercise One: Feature works, task creation and update + +## Related files +- app.js +- cli.js +- models.js + +## Propt +### Prompt used +I am trying to understand the task creation and status update of a project, it works with Javascript and a bit of Node.js and CLI. +The feature seems to allow the user to create a new task and update its completion status of the task manager app + +Here are the key feature: +app.js- Main entry point +cli.js- Handles terminal side +models.js- handles logic for the tasks + +Here are the main code snipets: + +```javascript + updateTaskStatus(taskId, newStatusValue) { + if (newStatusValue === TaskStatus.DONE) { + const task = this.storage.getTask(taskId); + if (task) { + task.markAsDone(); + this.storage.save(); + return true; + } + return false; + } else { + return this.storage.updateTask(taskId, { status: newStatusValue }); + } + } +``` + + +```javascript +createTask(title, description = "", priorityValue = 2, dueDateStr = null, tags = []) { + const priority = priorityValue; + let dueDate = null; + + if (dueDateStr) { + try { + dueDate = new Date(dueDateStr); + if (isNaN(dueDate.getTime())) { + throw new Error("Invalid date"); + } + } catch (error) { + console.error("Invalid date format. Use YYYY-MM-DD"); + return null; + } + } + + const task = new Task(title, description, priority, dueDate, tags); + const taskId = this.storage.addTask(task); + return taskId; + } +``` + +### Prompt response +# Task Creation and Status Update Flow + +## Overview + +The task manager splits responsibilities across three files: + +- **`cli.js`** — parses terminal input and calls app-level functions +- **`app.js`** — contains `createTask()` and `updateTaskStatus()` business logic +- **`models.js`** — defines the `Task` model and its methods (e.g. `markAsDone()`) + +--- + +## `createTask()` + +```javascript +createTask(title, description = "", priorityValue = 2, dueDateStr = null, tags = []) { + const priority = priorityValue; + let dueDate = null; + + if (dueDateStr) { + try { + dueDate = new Date(dueDateStr); + if (isNaN(dueDate.getTime())) { + throw new Error("Invalid date"); + } + } catch (error) { + console.error("Invalid date format. Use YYYY-MM-DD"); + return null; + } + } + + const task = new Task(title, description, priority, dueDate, tags); + const taskId = this.storage.addTask(task); + return taskId; +} +``` + +### What it does + +1. **Validates the due date** — wraps `new Date(dueDateStr)` in a try/catch and also checks `isNaN(dueDate.getTime())`. This catches both thrown errors and silently invalid dates (e.g. `new Date("banana")` returns an invalid Date object rather than throwing). +2. **Constructs a `Task` object** — passes the validated fields into the `Task` model constructor. +3. **Persists via storage** — calls `storage.addTask(task)` and returns the new `taskId`. + +### Important edge case + +If the date is invalid, `createTask` returns `null`. The caller in `cli.js` should check for this before confirming a task to the user. + +--- + +## `updateTaskStatus()` + +```javascript +updateTaskStatus(taskId, newStatusValue) { + if (newStatusValue === TaskStatus.DONE) { + const task = this.storage.getTask(taskId); + if (task) { + task.markAsDone(); + this.storage.save(); + return true; + } + return false; + } else { + return this.storage.updateTask(taskId, { status: newStatusValue }); + } +} +``` + +### What it does + +The function splits into two paths depending on the incoming status: + +| Status value | Path taken | +|---|---| +| `TaskStatus.DONE` | Fetch task → call `task.markAsDone()` → call `storage.save()` | +| Any other status | Call `storage.updateTask(taskId, { status: newStatusValue })` directly | + +### Why `DONE` is handled differently + +The `DONE` branch uses a three-step pattern — `getTask()` → `markAsDone()` → `save()` — rather than a simple field update. This suggests `markAsDone()` on the model does more than flip a status field. It likely: + +- Sets a `completedAt` timestamp +- Runs pre-completion validations +- Triggers side effects that a plain `updateTask({ status: 'done' })` would bypass + +--- + +## Flow Summary + +``` +cli.js + │ + ├── create command ──► createTask() + │ │ + │ ├── validate date + │ ├── new Task(...) + │ └── storage.addTask() ──► return taskId + │ + └── update command ──► updateTaskStatus() + │ + ├── status === DONE? + │ YES → storage.getTask() + │ └── task.markAsDone() + │ └── storage.save() + │ + └── NO → storage.updateTask({ status }) +``` + +--- + +## Key Takeaways + +- `createTask` is **defensive** — it handles both thrown errors and invalid Date objects. +- `updateTaskStatus` uses a **model method** (`markAsDone`) for the `DONE` transition, keeping domain logic in `models.js` rather than leaking it into `app.js`. +- The `storage.save()` call is **explicit** in the `DONE` path but delegated inside `storage.updateTask()` for all other statuses — worth confirming this symmetry is intentional. + +## Main feature +### Main Components +- Task Creation +- Task Status Update +### Execution Flow +1. User runs a CLI command to create or update a task. +2. `cli.js` parses the command and calls the appropriate function in `app.js`. +3. For task creation: + - `createTask()` validates the due date, constructs a `Task` object, and saves it via `storage.addTask()`. +4. For status updates: + - If marking as `DONE`, it retrieves the task, calls `markAsDone()`, and saves. + - For other statuses, it directly updates the status via `storage.updateTask()`. +### How data is stored and retrieved +- Tasks are stored via the `storage` object, which likely interacts with a file or database. +- `getTask(taskId)` retrieves a specific task, while `addTask(task)` saves a new task and returns its ID. `updateTask(taskId, updates)` modifies existing tasks based on the provided fields. +### Interesting Patterns +- The use of a model method (`markAsDone()`) for a specific status transition suggests encapsulation of domain logic within the `Task` class. +- The dual validation of the due date in `createTask()` (try/catch + `isNaN()`) is a robust pattern for handling user input that can fail in multiple ways. \ No newline at end of file From efbc2f9a46fb266c11429cf0220d2c4ee514d365 Mon Sep 17 00:00:00 2001 From: ShaunvdBijl Date: Thu, 21 May 2026 11:38:50 +0200 Subject: [PATCH 6/8] AlgorithmDeconstruct --- .../javascript/Algorithm/algorithmExercise.md | 355 ++++++++++++++++++ .../javascript/{ => WhereToStart}/dataFlow.md | 0 .../{ => WhereToStart}/deepenUnderstanding.md | 0 .../{ => WhereToStart}/documentation.md | 0 .../{ => WhereToStart}/domainModel.md | 0 .../{ => WhereToStart}/finalDiscussion.md | 0 .../{ => WhereToStart}/newFeature.md | 0 .../{ => WhereToStart}/practical.md | 0 .../{ => WhereToStart}/reflextion.md | 0 .../{ => WhereToStart}/specificFeature.md | 0 .../{ => WhereToStart}/submission.md | 0 11 files changed, 355 insertions(+) create mode 100644 use-cases/task-manager/javascript/Algorithm/algorithmExercise.md rename use-cases/task-manager/javascript/{ => WhereToStart}/dataFlow.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/deepenUnderstanding.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/documentation.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/domainModel.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/finalDiscussion.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/newFeature.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/practical.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/reflextion.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/specificFeature.md (100%) rename use-cases/task-manager/javascript/{ => WhereToStart}/submission.md (100%) diff --git a/use-cases/task-manager/javascript/Algorithm/algorithmExercise.md b/use-cases/task-manager/javascript/Algorithm/algorithmExercise.md new file mode 100644 index 00000000..3ff44314 --- /dev/null +++ b/use-cases/task-manager/javascript/Algorithm/algorithmExercise.md @@ -0,0 +1,355 @@ +# Deconstruct Algorithm Exercise + +## Context +I previously pasted an algorithm into Claude and asked it to use that algorithm for the conversation. + +## My prompt +I'm trying to understand this algorithm/function in our codebase. + +What I know: +- The function appears to merge a remote task and a local task while resolving conflicts to ensure a proper merge. +- Inputs likely include: ids (local and remote), local and remote task objects, and local and remote dates. +- I'm particularly puzzled by the resolveTaskConflict function. + +Could you help me understand this by: +1. Breaking down the algorithm into key sections with their purposes +2. Walking through a simple example execution with concrete values +3. Explaining the core technique/pattern being used here +4. Highlighting any non-obvious optimizations or tricks + +After your explanation, please ask 2–3 targeted questions that test my understanding of the algorithm's: +- Underlying principles +- Edge cases +- Performance characteristics + +This will help me ensure I've really grasped how it works. + +## Prompt Response +# Understanding `resolveTaskConflict` + +You're right about the overall purpose — but let me sharpen your mental model of the inputs. The function actually takes two **full task objects** (not separate IDs/dates). The dates are extracted *inside* the function from `task.updatedAt`. + +--- + +## 1. Key Sections Breakdown + +### Section A — Base Setup + +```javascript +const mergedTask = {...localTask}; +let shouldUpdateLocal = false; +let shouldUpdateRemote = false; +``` + +Uses local as the "working copy." Two flags track whether either source needs syncing back. This is the **base + patch** pattern. + +--- + +### Section B — Timestamp-Wins for Core Fields + +```javascript +const localDate = new Date(localTask.updatedAt); +const remoteDate = new Date(remoteTask.updatedAt); + +if (remoteDate > localDate) { + mergedTask.title = remoteTask.title; + ... + shouldUpdateLocal = true; +} else { + shouldUpdateRemote = true; // local is newer or equal +} +``` + +A single timestamp comparison governs title, description, priority, and dueDate — whichever was touched more recently wins wholesale. + +--- + +### Section C — Completion Status Override + +```javascript +if (remoteTask.status === DONE && localTask.status !== DONE) { + mergedTask.status = DONE; // remote completion wins + shouldUpdateLocal = true; +} else if (localTask.status === DONE ...) { + shouldUpdateRemote = true; // local completion wins +} else { + // both non-DONE: fall back to timestamp winner +} +``` + +Completion is treated as **irreversible** — DONE always beats any other status, regardless of timestamps. This runs *after* Section B, so it can override the timestamp decision for status specifically. + +--- + +### Section D — Tag Union + +```javascript +const allTags = [...new Set([...localTask.tags, ...remoteTask.tags])]; +``` + +Tags are **additive** — neither source can delete the other's tags. Both sides get updated if their tag list differs from the union. + +--- + +### Section E — Final Timestamp + +```javascript +mergedTask.updatedAt = localDate > remoteDate ? localTask.updatedAt : remoteTask.updatedAt; +``` + +Stamps the merged result with whichever timestamp was most recent. + +--- + +## 2. Concrete Example Walkthrough + +```javascript +localTask = { + id: "task-1", + title: "Write tests", + status: "IN_PROGRESS", + tags: ["backend", "urgent"], + updatedAt: "2024-01-10T09:00:00Z" // older +} + +remoteTask = { + id: "task-1", + title: "Write unit tests", // renamed + status: "DONE", // completed remotely + tags: ["backend", "qa"], // different tag + updatedAt: "2024-01-10T11:00:00Z" // newer +} +``` + +| Step | Decision | Result | +|---|---|---| +| Base copy | Start with localTask | `title: "Write tests"` | +| Timestamp check | remote (11:00) > local (09:00) | Copy remote title/desc/priority → `title: "Write unit tests"` | +| Status check | remote is DONE, local isn't | Override → `status: DONE` | +| Tag union | `{backend, urgent} ∪ {backend, qa}` | `["backend", "urgent", "qa"]` | +| Flags | local needs status + tag + title update | `shouldUpdateLocal: true`, `shouldUpdateRemote: true` (tags differ) | + +--- + +## 3. Core Pattern: Last-Write-Wins + Semantic Overrides + +This is a classic **LWW (Last-Write-Wins) with domain exceptions** — a common CRDT-adjacent strategy: + +- **LWW** handles most fields: simple, fast, predictable +- **Semantic overrides** handle fields where business logic trumps recency — completion is *meaningful* state that shouldn't be silently reversed by a stale local timestamp + +The tag union is a **grow-only set** — another CRDT primitive — meaning tags can only be added through normal sync, never accidentally removed. + +--- + +## 4. Non-Obvious Tricks + +### The Flag Symmetry Problem + +Notice this scenario in Section B: + +```javascript +} else { + // local is newer OR equal → shouldUpdateRemote = true +} +``` + +When timestamps are *identical*, it defaults to "local wins" and pushes to remote. This is a **silent tiebreaker** — not documented, but intentional. + +--- + +### Status Can Contradict the Timestamp Winner + +Section C runs independently of Section B. So the merged task could end up with remote's title (B) *and* local's completion status (C), producing a result **neither source had exactly**. + +--- + +### Tag Comparison Uses Sorted Arrays + +`arraysEqual` sorts before comparing, so `["urgent", "backend"]` and `["backend", "urgent"]` are correctly treated as identical. Without this, spurious updates would be triggered constantly. + +--- + +## Test Your Understanding + +Here are 3 questions to check your grasp: + +1. **Edge case:** Both `localTask` and `remoteTask` have `status: "IN_PROGRESS"` and *identical* `updatedAt` timestamps. Walk through exactly what `resolveTaskConflict` returns — what are the flag values and why? + +2. **Principle:** Why are tags handled with a union instead of letting the newer timestamp win (like title and description)? What real-world sync scenario does this protect against? + +3. **Performance/correctness:** `shouldUpdateRemote` can be set to `true` in Section B, then `true` again in Section D. Is there any case where Section B sets `shouldUpdateRemote = false` and Section D later overrides it to `true`? Trace the code to explain why or why not. + +--- + +## Selected Algorithm: Task List Merging (Two-way Sync) + +I selected the task list merging algorithm because it matches the `resolveTaskConflict` logic previously analysed. This document consolidates the AI explanation, adds diagrams, gives a concrete step-by-step walkthrough with values, surfaces insights, and answers the reflection questions you requested. + +### Short description +Merge two versions of the same task (local and remote) into a single canonical task record, resolving conflicts using timestamp-wins for most fields, semantic overrides for status, and a union strategy for tags. + +## 1) Broken-down algorithm sections (using the AI prompt approach) + +- Section A — Base copy and flags + - Purpose: Start with the local task as the working copy and prepare `shouldUpdateLocal` and `shouldUpdateRemote` flags to indicate which side needs syncing after merge. + +- Section B — Timestamp-based winner for core fields + - Purpose: Compare `updatedAt` timestamps and copy title/description/priority/dueDate from the newer side into the merged result. + - Reasoning: Simple LWW is fast and deterministic for fields without domain-specific invariants. + +- Section C — Semantic override for completion + - Purpose: Treat `DONE` as a dominantly meaningful state: if either side has `status === DONE`, the merged task becomes `DONE` regardless of timestamps. + - Reasoning: Completion is irreversible in business terms and should not be reverted by an older change. + +- Section D — Tag union (grow-only merge) + - Purpose: Compute the union of local and remote tags; neither side deletes the other's tags. + - Reasoning: Tags represent labels a user may add; merging should preserve both sides' labels. + +- Section E — Final timestamp assignment + - Purpose: Set `merged.updatedAt` to the newest timestamp of the two inputs. + +## 2) Visual diagrams + +Merger overview (Mermaid sequence): + +```mermaid +sequenceDiagram + participant CLI + participant LocalStore + participant Merger + participant RemoteStore + + CLI->>LocalStore: get localTask(id) + CLI->>RemoteStore: get remoteTask(id) + LocalStore->>Merger: send localTask + RemoteStore->>Merger: send remoteTask + Merger->>Merger: compare timestamps, status, tags + Merger-->>LocalStore: update local if needed + Merger-->>RemoteStore: update remote if needed + LocalStore-->>CLI: mergedTask +``` + +Field-level decision flow (ASCII): + +- If remote.updatedAt > local.updatedAt then + - copy title/desc/priority/dueDate from remote + - set shouldUpdateLocal = true +- Else + - copy fields from local + - set shouldUpdateRemote = true +- Then handle `status` specially (DONE overrides) +- Then tags = union(local.tags, remote.tags) + +## 3) Concrete example walkthrough (with concrete values) + +Inputs: + +localTask: + +``` +id: "t1" +title: "Draft report" +description: "start draft" +status: "IN_PROGRESS" +tags: ["work","draft"] +priority: 2 +updatedAt: "2026-05-20T09:00:00Z" +``` + +remoteTask: + +``` +id: "t1" +title: "Draft report" +description: "start draft and include stats" +status: "DONE" +tags: ["work","review"] +priority: 2 +updatedAt: "2026-05-20T10:30:00Z" +``` + +Merge steps and rationale: + +1. Base: merged = shallow copy of `localTask`. +2. Timestamps: remote is newer (10:30 > 09:00) → copy `description` from remote; set `shouldUpdateLocal = true`. +3. Status override: remote `status === DONE` → merged.status = `DONE`; ensure `shouldUpdateLocal = true`. +4. Tags: union(["work","draft"], ["work","review"]) → ["work","draft","review"]; if localTags differ from union, set `shouldUpdateLocal = true`; if remote differs, set `shouldUpdateRemote = true`. +5. Final timestamp: merged.updatedAt = remote.updatedAt (10:30). + +Resulting merged task: + +``` +id: "t1" +title: "Draft report" +description: "start draft and include stats" +status: "DONE" +tags: ["work","draft","review"] +priority: 2 +updatedAt: "2026-05-20T10:30:00Z" +``` + +Flags: +- shouldUpdateLocal = true +- shouldUpdateRemote = true (if remote lacks any of the union tags — here remote lacks `draft`) + +## 4) Core technique / pattern used + +- Primary pattern: Last-Write-Wins (LWW) for simple fields. +- Domain exception: Semantic override (completion is irreversible) — a business-rule-driven exception to LWW. +- CRDT-esque primitive: Tag union is a grow-only set (G-Set) behavior. + +Design trade-offs: + +- Pros: Simple, deterministic, low compute overhead; easy to reason about. +- Cons: LWW can lose intentful edits if clocks are skewed or users edit concurrently; ties break arbitrarily (often in favour of local). + +## 5) Non-obvious optimizations and implementation notes + +- Tiebreaker behavior: identical timestamps default to a particular side (often local). Make this explicit for maintainability. +- Tags are compared using sorted arrays to avoid spurious updates due to ordering differences. +- Status is handled after field LWW so it can override the previous copy for `status` only. + +## 6) Insights and learning points + +- Domain rules matter: Not all fields are equal—`status` (DONE) encodes irreversible business meaning and must be treated specially. +- Symmetry vs. practicality: The algorithm aims for symmetry (both `shouldUpdateLocal` and `shouldUpdateRemote` flags) but must also pick deterministic tie-breaking rules. +- Simple CRDT primitives (LWW + G-Set) are pragmatic in lightweight sync scenarios where full CRDTs are too heavy. + +## 7) Answers to the reflection questions + +Q: How did the AI’s explanation change your understanding of the algorithm? + +A: The AI clarified that the function uses a hybrid approach: LWW for most fields, a semantic override for `DONE`, and a union for tags. It also highlighted the implicit tie-breaker behavior (local wins on equal timestamps) and the rationale for choosing different merge strategies per field. + +Q: What aspects were still difficult after the AI explanation? + +A: Two areas remained subtle: +- Clock skew and its effect on LWW correctness (no vector clocks or causal metadata present). +- The exact expectations for `shouldUpdateLocal` vs `shouldUpdateRemote` when multiple fields differ — the algorithm sets flags conservatively but the sync semantics (how often to push updates, conflict storms) depend on higher-level sync orchestration. + +Q: How would you explain this algorithm to a junior developer? + +A: "We take the newest edit for ordinary fields (title, description, due date) to keep things simple. But if either side says the task is DONE, we trust that because completing a task is a meaningful action we don't want to accidentally undo. For tags, we merge both sides so no one loses labels. After merging, we record which side needs to be updated so the stores can be synchronized." Keep the explanation concrete with an example. + +Q: Did you test this understanding against AI? + +A: Yes — the AI walkthrough and the questions it suggested were used to validate edge cases (identical timestamps, tag ordering) and to surface implementation subtleties like tie-breaking and sorting during comparisons. + +Q: How might you improve the algorithm based on your understanding? + +A: Improvements to consider: + +- Add causal metadata (vector clocks or operation timestamps) to avoid relying solely on wall-clock LWW. +- Make tie-breaker rules explicit and configurable (e.g., prefer remote or prefer higher node id). +- Consider adding an audit log or change set so merges are replayable and reversible. +- If tag deletions are required, move from G-Set to a two-phase set (add/remove with tombstones) to support deletions safely. + +## 8) Suggested follow-ups / tests + +- Unit tests for: identical timestamps, both DONE vs one DONE, tag ordering differences, disjoint tags, and clock-skewed timestamps. +- Integration test: simulate rapid local & remote edits and verify the merged outcome and flags. + +--- + +_Document updated with the two-way sync / merge analysis and reflections._ \ No newline at end of file diff --git a/use-cases/task-manager/javascript/dataFlow.md b/use-cases/task-manager/javascript/WhereToStart/dataFlow.md similarity index 100% rename from use-cases/task-manager/javascript/dataFlow.md rename to use-cases/task-manager/javascript/WhereToStart/dataFlow.md diff --git a/use-cases/task-manager/javascript/deepenUnderstanding.md b/use-cases/task-manager/javascript/WhereToStart/deepenUnderstanding.md similarity index 100% rename from use-cases/task-manager/javascript/deepenUnderstanding.md rename to use-cases/task-manager/javascript/WhereToStart/deepenUnderstanding.md diff --git a/use-cases/task-manager/javascript/documentation.md b/use-cases/task-manager/javascript/WhereToStart/documentation.md similarity index 100% rename from use-cases/task-manager/javascript/documentation.md rename to use-cases/task-manager/javascript/WhereToStart/documentation.md diff --git a/use-cases/task-manager/javascript/domainModel.md b/use-cases/task-manager/javascript/WhereToStart/domainModel.md similarity index 100% rename from use-cases/task-manager/javascript/domainModel.md rename to use-cases/task-manager/javascript/WhereToStart/domainModel.md diff --git a/use-cases/task-manager/javascript/finalDiscussion.md b/use-cases/task-manager/javascript/WhereToStart/finalDiscussion.md similarity index 100% rename from use-cases/task-manager/javascript/finalDiscussion.md rename to use-cases/task-manager/javascript/WhereToStart/finalDiscussion.md diff --git a/use-cases/task-manager/javascript/newFeature.md b/use-cases/task-manager/javascript/WhereToStart/newFeature.md similarity index 100% rename from use-cases/task-manager/javascript/newFeature.md rename to use-cases/task-manager/javascript/WhereToStart/newFeature.md diff --git a/use-cases/task-manager/javascript/practical.md b/use-cases/task-manager/javascript/WhereToStart/practical.md similarity index 100% rename from use-cases/task-manager/javascript/practical.md rename to use-cases/task-manager/javascript/WhereToStart/practical.md diff --git a/use-cases/task-manager/javascript/reflextion.md b/use-cases/task-manager/javascript/WhereToStart/reflextion.md similarity index 100% rename from use-cases/task-manager/javascript/reflextion.md rename to use-cases/task-manager/javascript/WhereToStart/reflextion.md diff --git a/use-cases/task-manager/javascript/specificFeature.md b/use-cases/task-manager/javascript/WhereToStart/specificFeature.md similarity index 100% rename from use-cases/task-manager/javascript/specificFeature.md rename to use-cases/task-manager/javascript/WhereToStart/specificFeature.md diff --git a/use-cases/task-manager/javascript/submission.md b/use-cases/task-manager/javascript/WhereToStart/submission.md similarity index 100% rename from use-cases/task-manager/javascript/submission.md rename to use-cases/task-manager/javascript/WhereToStart/submission.md From 7e2c9d07efe817d43169b707badddf95d5c119f6 Mon Sep 17 00:00:00 2001 From: ShaunvdBijl Date: Thu, 21 May 2026 11:41:19 +0200 Subject: [PATCH 7/8] Algorithm and File struture --- .../javascript/{WhereToStart => Codebase}/dataFlow.md | 0 .../javascript/{WhereToStart => Codebase}/deepenUnderstanding.md | 0 .../javascript/{WhereToStart => Codebase}/reflextion.md | 0 .../javascript/{WhereToStart => Codebase}/specificFeature.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename use-cases/task-manager/javascript/{WhereToStart => Codebase}/dataFlow.md (100%) rename use-cases/task-manager/javascript/{WhereToStart => Codebase}/deepenUnderstanding.md (100%) rename use-cases/task-manager/javascript/{WhereToStart => Codebase}/reflextion.md (100%) rename use-cases/task-manager/javascript/{WhereToStart => Codebase}/specificFeature.md (100%) diff --git a/use-cases/task-manager/javascript/WhereToStart/dataFlow.md b/use-cases/task-manager/javascript/Codebase/dataFlow.md similarity index 100% rename from use-cases/task-manager/javascript/WhereToStart/dataFlow.md rename to use-cases/task-manager/javascript/Codebase/dataFlow.md diff --git a/use-cases/task-manager/javascript/WhereToStart/deepenUnderstanding.md b/use-cases/task-manager/javascript/Codebase/deepenUnderstanding.md similarity index 100% rename from use-cases/task-manager/javascript/WhereToStart/deepenUnderstanding.md rename to use-cases/task-manager/javascript/Codebase/deepenUnderstanding.md diff --git a/use-cases/task-manager/javascript/WhereToStart/reflextion.md b/use-cases/task-manager/javascript/Codebase/reflextion.md similarity index 100% rename from use-cases/task-manager/javascript/WhereToStart/reflextion.md rename to use-cases/task-manager/javascript/Codebase/reflextion.md diff --git a/use-cases/task-manager/javascript/WhereToStart/specificFeature.md b/use-cases/task-manager/javascript/Codebase/specificFeature.md similarity index 100% rename from use-cases/task-manager/javascript/WhereToStart/specificFeature.md rename to use-cases/task-manager/javascript/Codebase/specificFeature.md From be872ee8b457aa328ce2458e42a49522e8a6185b Mon Sep 17 00:00:00 2001 From: ShaunvdBijl Date: Fri, 22 May 2026 10:01:35 +0200 Subject: [PATCH 8/8] All Exercises until debugging --- API-Documentation/express.js | 104 ++++++++++++++++++ API-Documentation/walkthrough.md | 0 Error-Debug/dependancy.md | 0 Error-Debug/guide.md | 0 Error-Debug/indexOutOfBounds.js | 0 Error-Debug/learning_solutions.md | 55 +++++++++ Error-Debug/rootCause.md | 0 .../TaskManager/codeDocumentation.md | 0 use-cases/task-manager/CLIGuide.md | 0 use-cases/task-manager/FAQ.md | 0 use-cases/task-manager/README.md | 0 11 files changed, 159 insertions(+) create mode 100644 API-Documentation/express.js create mode 100644 API-Documentation/walkthrough.md create mode 100644 Error-Debug/dependancy.md create mode 100644 Error-Debug/guide.md create mode 100644 Error-Debug/indexOutOfBounds.js create mode 100644 Error-Debug/learning_solutions.md create mode 100644 Error-Debug/rootCause.md create mode 100644 use-cases/code-algorithms/javascript/TaskManager/codeDocumentation.md create mode 100644 use-cases/task-manager/CLIGuide.md create mode 100644 use-cases/task-manager/FAQ.md create mode 100644 use-cases/task-manager/README.md diff --git a/API-Documentation/express.js b/API-Documentation/express.js new file mode 100644 index 00000000..1faa310d --- /dev/null +++ b/API-Documentation/express.js @@ -0,0 +1,104 @@ +/** + * Product API endpoints + */ +const productRouter = express.Router(); + +// Get all products with filtering and pagination +productRouter.get('/', async (req, res) => { + try { + const { + category, + minPrice, + maxPrice, + sort = 'createdAt', + order = 'desc', + page = 1, + limit = 20, + inStock + } = req.query; + + // Build filter + const filter = {}; + + if (category) { + filter.category = category; + } + + if (minPrice !== undefined || maxPrice !== undefined) { + filter.price = {}; + if (minPrice !== undefined) filter.price.$gte = parseFloat(minPrice); + if (maxPrice !== undefined) filter.price.$lte = parseFloat(maxPrice); + } + + if (inStock === 'true') { + filter.stockQuantity = { $gt: 0 }; + } + + // Calculate pagination + const skip = (parseInt(page) - 1) * parseInt(limit); + + // Determine sort order + const sortOptions = {}; + sortOptions[sort] = order === 'asc' ? 1 : -1; + + // Execute query + const products = await ProductModel.find(filter) + .sort(sortOptions) + .skip(skip) + .limit(parseInt(limit)); + + // Get total count for pagination + const totalProducts = await ProductModel.countDocuments(filter); + + return res.status(200).json({ + products, + pagination: { + total: totalProducts, + page: parseInt(page), + limit: parseInt(limit), + pages: Math.ceil(totalProducts / parseInt(limit)) + } + }); + } catch (error) { + console.error('Error fetching products:', error); + return res.status(500).json({ + error: 'Server error', + message: 'Failed to fetch products' + }); + } +}); + +// Get product by ID +productRouter.get('/:productId', async (req, res) => { + try { + const { productId } = req.params; + + const product = await ProductModel.findById(productId); + + if (!product) { + return res.status(404).json({ + error: 'Not found', + message: 'Product not found' + }); + } + + return res.status(200).json(product); + } catch (error) { + console.error('Error fetching product:', error); + + // Check if error is invalid ObjectId format + if (error.name === 'CastError') { + return res.status(400).json({ + error: 'Invalid ID', + message: 'Invalid product ID format' + }); + } + + return res.status(500).json({ + error: 'Server error', + message: 'Failed to fetch product' + }); + } +}); + +module.exports = productRouter; \ No newline at end of file diff --git a/API-Documentation/walkthrough.md b/API-Documentation/walkthrough.md new file mode 100644 index 00000000..e69de29b diff --git a/Error-Debug/dependancy.md b/Error-Debug/dependancy.md new file mode 100644 index 00000000..e69de29b diff --git a/Error-Debug/guide.md b/Error-Debug/guide.md new file mode 100644 index 00000000..e69de29b diff --git a/Error-Debug/indexOutOfBounds.js b/Error-Debug/indexOutOfBounds.js new file mode 100644 index 00000000..e69de29b diff --git a/Error-Debug/learning_solutions.md b/Error-Debug/learning_solutions.md new file mode 100644 index 00000000..170730e0 --- /dev/null +++ b/Error-Debug/learning_solutions.md @@ -0,0 +1,55 @@ + +# Proposed Fixes and Learning Points + +## Solution + +- Problem location: [use-cases/debug-errors-001/javascript/userList.js](use-cases/debug-errors-001/javascript/userList.js#L1-L200) +- Root cause: the code used a hardcoded loop `for (let i = 0; i < 5; i++)` and indexed into `users[i]`, which can be `undefined` when fewer than 5 users are returned. +- Code change (replace the unsafe loop with defensive iteration): + +```javascript +// ❌ Unsafe original +for (let i = 0; i < 5; i++) { + const user = users[i]; + const userName = user.name; // throws if user is undefined + ... +} + +// ✅ Safe replacement +if (!Array.isArray(users) || users.length === 0) { + userListElement.innerHTML = '

No users found.

'; + return; +} + +users.forEach(user => { + if (!user) return; + const userName = user.name || 'Unknown'; + const userEmail = user.email || ''; + // render user card +}); +``` + +- Other small improvements included in the patch: + - Guarded against non-array or empty `users` input. + - Used safe property fallbacks (`user.name || 'Unknown'`). + - Skipped undefined entries to prevent crashes. + +## Learning Points + +- Always validate external data shapes before indexing or property access. Use `Array.isArray()` and check `.length`. +- Avoid magic numbers for iteration bounds. Use `array.length` or iteration helpers (`forEach`, `for...of`). +- Provide meaningful UX when data is empty (e.g., show "No users found."). +- Use safe property access patterns (`user?.name` or `user.name || 'Unknown'`) to prevent runtime exceptions. +- Add unit tests that cover edge cases: zero items, one item, many items, and malformed input (null/undefined/non-array). +- Consider linting (ESLint) and light static typing (JSDoc or TypeScript) to catch these mistakes early. + +## Suggested Tests & Next Steps + +- Add a unit test (Jest + JSDOM) that calls `renderUserList([])` and asserts the DOM contains "No users found.". +- Add tests for `renderUserList([null, {name: 'A'}])` to ensure undefined entries are skipped. +- Search the repo for other hardcoded loops that index external data and apply similar defensive checks. + +--- + +Document created to record the proposed change and to help prevent similar errors in the future. + diff --git a/Error-Debug/rootCause.md b/Error-Debug/rootCause.md new file mode 100644 index 00000000..e69de29b diff --git a/use-cases/code-algorithms/javascript/TaskManager/codeDocumentation.md b/use-cases/code-algorithms/javascript/TaskManager/codeDocumentation.md new file mode 100644 index 00000000..e69de29b diff --git a/use-cases/task-manager/CLIGuide.md b/use-cases/task-manager/CLIGuide.md new file mode 100644 index 00000000..e69de29b diff --git a/use-cases/task-manager/FAQ.md b/use-cases/task-manager/FAQ.md new file mode 100644 index 00000000..e69de29b diff --git a/use-cases/task-manager/README.md b/use-cases/task-manager/README.md new file mode 100644 index 00000000..e69de29b