Patterns in the Pantry: Rediscovering Our Club Oven Lovin’ Architecture After M2

03 Dec 2025

From Thanksgiving Layover to Design Pattern Do-Over

Milestone 2 (M2) went by in a blur. I spent that stretch bouncing between Boston job interviews and Thanksgiving travel, which meant I wasn’t as present in the repo as I wanted to be. Coming back now feels like walking into a kitchen after service: the dishes are stacked, the ingredients are half-prepped, and the aroma tells me a lot about what actually happened while I was gone.

This essay is my way of catching uplooking at our Club Oven Lovin’ codebase through “design pattern glasses” to understand what we built, where patterns already peek through, and how a more deliberate pattern mindset could fuel Milestone 3 (M3).

Patterns Are Recipes for Repeatable Success

Design patterns are to software what recipes are to cooking: they capture proven arrangements of ingredients and steps so teams don’t have to reinvent the dish every time. More importantly, they give us a shared vocabulary. Saying “let’s use a repository here” or “this looks like a container component” lets teammates coordinate quickly, just like cooks calling “mise en place” or “fire two risottos.”

Patterns are not rigid templates; they’re heuristics that balance structure with flexibility. Used well, they prevent the project from devolving into ad hoc decisions that don’t scale. In the context of our final project, patterns are the difference between a one-off prototype and a codebase that can survive new features, new teammates, and new bugs.

The M2 Menu We Promised

Officially, M2 came with two big promises: richer functionality and a stronger software engineering process.

On the functional side, we committed to:

On the process side, we were supposed to:

Hidden inside this requirements list is a whole cookbook of patterns: routing and page modularity (architectural patterns), form-handling and validation (strategy/template patterns), data-access consistency (repository pattern), and testing/CI (deployment pipeline and process patterns).

What Actually Hit Production (and How It’s Structured)

Opening the repo now, I see a Next.js app with a layered feel:

To make this less abstract, here’s what our repository-like consolidation looks like in practice:

// src/lib/dbActions.ts
export async function addRecipe(recipe: {
  name: string;
  image: string;
  ingredients: string;
  steps: string;
  tags: string;
  dietaryRestrictions: string[];
  owner: string;
}) {
  await prisma.recipe.create({
    data: {
      name: recipe.name,
      image: recipe.image,
      ingredients: recipe.ingredients,
      steps: recipe.steps,
      tags: recipe.tags.split(",").map((t) => t.trim()),
      dietaryRestrictions: recipe.dietaryRestrictions,
      owner: recipe.owner,
    },
  });

  redirect("/browse-recipes");
}

All write logic for recipes is in one place; pages call this without caring about Prisma details. That’s exactly the value of a Repository: we could swap Prisma later or add logging without touching the call sites.

Form handling shows a Template/Strategy flavor. In AddRecipeForm, react-hook-form owns the workflowvalidation, submission, error collectionwhile we configure it with a schema and field registrations:

// src/components/AddRecipeForm.tsx
const {
  register,
  handleSubmit,
  formState: { errors },
  reset,
} = useForm<AddRecipeFormData>({
  resolver: yupResolver(AddRecipeSchema),
});

return (
  <Form onSubmit={handleSubmit(onSubmit)} data-testid="add-recipe-form">
    <Form.Control type="text" {...register("name")} isInvalid={!!errors.name} />
    <Form.Control.Feedback type="invalid">
      {errors.name?.message}
    </Form.Control.Feedback>
    {/* ...other fields... */}
  </Form>
);

The hook provides the algorithmic skeleton; we supply strategies (validators) and concrete fields. That aligns with the “don’t repeat form boilerplate” ethos.

Authorization shows a mini guard pattern. adminProtectedPage composes loggedInProtectedPage and a role check to redirect users appropriately. Instead of scattering if (!session) redirect() checks in every page, we call a shared guard. It’s simple, but it’s a recognizable pattern.

Here’s an example of the UI this architecture supports:

Browse recipes page showing Club Oven Lovin’ recipe cards with images, tags, and dietary badges
Club Oven Lovin’ Browse Recipes page: container layout feeding data into reusable recipe cards with tags and dietary badges.

Even this screenshot reflects patterns: container pages fetch data and pass it into smaller, reusable cards that know nothing about Prisma or routing.

Hidden Patterns in Our M2 Delivery

Connecting the dots:

In other words, even though we didn’t explicitly shout “we’re using the Repository pattern!” during M2, our code still grew into shapes that resemble known design patterns.

If I Had Been Fully There for M2…

Being partially absent meant I missed chances to standardize our patterns. Looking now, there are several places where I would push further if I had been fully present:

Each of these changes would have made M2 smoother: Playwright tests benefit from consistent loading/error UX, CI becomes less flaky when data access is centralized, and pull request reviews move faster when the team shares a pattern vocabulary.

Patterns as a Shared Language for Process

One surprising insight is how process requirements map to patterns:

Recognizing these as patterns makes me appreciate that software engineering is not just code patterns; it’s also collaboration patterns that keep the team aligned.

Looking Forward to M3

For M3, I want us to:

These moves will make adding new pages less painful and keep CI/Playwright stable because behavior becomes more predictable.

What I Learned About Design Patterns Here

Coming back after M2, I’m reminded that patterns are less about academic labels and more about reducing cognitive load. They:

Design patterns are the recipes that keep our kitchen humming. I’m excited to apply them more intentionally in M3 so that our Club Oven Lovin’ app scales in both flavor and maintainability.

Note on AI assistance

I used an AI writing assistant to help me generate an initial draft of this essay, iterate on the structure, and polish the wording. The ideas, code references, and project-specific content are based on our Club Oven Lovin’ codebase and my own understanding, and I reviewed and edited the AI-generated draft before submitting it.