DEV Community

Denner Fernandes
Denner Fernandes

Posted on

Beyond CRUD: building personal projects to validate architecture, cloud, and automation

For a long time, whenever someone mentioned a "portfolio project" in the tech world, the standard path was almost always the same: building a system featuring user registration, login, and the ability to list, edit, and delete records.

In other words: a CRUD application.

And there is nothing wrong with that.

CRUDs are important. They help demonstrate fundamentals, code organization, database integration, validation, authentication, UI, and basic application flow.

However, after years of working on development, integrations, business logic, infrastructure, automation, and production systems, I began to notice a limitation with this type of project: it doesn't always reveal how a person thinks in terms of architecture.

It shows that you know how to implement screens and endpoints.

But it doesn't always show that you know how to handle technical decisions that mirror real-world scenarios.

That is why I decided to revamp my public portfolio with a different approach: building personal projects that are small enough to maintain, yet rich enough to showcase architecture, cloud, automation, testing, domain modeling, and the practical application of AI.

The goal isn't to build "massive systems."
The goal is to create technical sandboxes that address real-world problems.


The problem with generic projects

Many portfolio projects end up stuck in a very similar structure:

  • user registration;
  • login;
  • a list view;
  • a creation form;
  • an edit button;
  • a delete button;
  • a simple dashboard;
  • basic deployment.

This might be enough to demonstrate foundational knowledge, but for those aiming to show seniority, a crucial layer may be missing: technical decision-making.

In practice, real-world systems often require addressing more complex questions:

  • How is the domain modeled?
  • How does the data evolve over time?
  • How does the system react to events?
  • How are failures handled?
  • How is auditing implemented?
  • How is infrastructure provisioned?
  • How are validations automated?
  • How are business rules decoupled from framework details? - How do you integrate external services without excessive coupling?
  • How do you test critical behaviors?
  • How do you document architectural decisions?

These questions are harder to demonstrate in a generic CRUD application.

That is why I started thinking about portfolio projects less as "apps to show off a UI" and more as software engineering laboratories.


What I want to demonstrate

My goal with these projects is to validate certain skills I consider important for backend, full-stack, cloud, and automation development:

  • domain modeling;
  • event-driven architecture;
  • TypeScript applied to business logic;
  • infrastructure as code;
  • use of cloud services;
  • CI/CD;
  • automated testing;
  • observability and auditing;
  • decoupled AI integration;
  • technical decision documentation;
  • product building with controlled scope.

The idea is for each public project to answer a clear technical question.

For example:

How do you model an asynchronous financial workflow using events?

Or:

How do you integrate AI into a product without locking business logic into loose prompts?

Or even:

How do you organize campaigns, content, metrics, and learning within a growth system?

These questions make the project more interesting than simply displaying a list of database records.


Personal projects don't have to be tiny

There is a common portfolio pitfall: thinking that a personal project needs to be extremely simple so it can be finished quickly.

I agree that the scope needs to be controlled. But "controlled" doesn't mean "superficial."

A small project can still feature sound architectural decisions.

It can include:

  • a well-defined domain;
  • typed events;
  • business logic tests;
  • clear documentation;
  • an architecture diagram;
  • a CI pipeline;
  • Terraform;
  • separation of application and infrastructure;
  • structured logs;
  • decisions recorded in ADRs.

None of this requires a massive system. The secret is to choose a small scope but treat it seriously.


Lab 1: Event-driven architecture with AWS

One of the projects I plan to build is a technical lab for credit portability using event-driven architecture.

The goal is to create an educational simulation of a financial workflow featuring multiple stages, state transitions, asynchronous events, and an audit trail.

The planned stack includes:

TypeScript
AWS EventBridge
AWS Lambda
Amazon SQS
DynamoDB
Terraform
GitHub Actions
Vitest
Enter fullscreen mode Exit fullscreen mode

The objective is not to implement an official specification or replicate a real-world regulatory system. The idea is to use a financial domain as a foundation to explore key technical concepts.

A simplified workflow might look like this:

Customer requests portability
        ↓
API logs the request
        ↓
Event: portability.request.created
        ↓
EventBridge routes the event
        ↓
Workers process asynchronous steps
        ↓
Request status is updated
        ↓
Audit events are logged
Enter fullscreen mode Exit fullscreen mode

Some possible events:

portability.request.created
consent.authorized
source.institution.notified
credit.contract.loaded
proposal.created
proposal.accepted
portability.completed
portability.rejected
portability.failed
Enter fullscreen mode Exit fullscreen mode

This type of project allows for demonstrating several relevant points:

  • event design;
  • separation of commands and events;
  • asynchronous processing;
  • routing with EventBridge;
  • queues with SQS;
  • Lambda functions;
  • persistence in DynamoDB;
  • provisioning with Terraform;
  • auditing;
  • idempotency;
  • error handling;
  • infrastructure automation.

It is technically much more interesting than simply registering proposals in a database table.


Terraform as part of the project, not just a detail

Another important point is treating infrastructure as part of the code.

Instead of just creating resources manually in the AWS console, the idea is to provision everything using Terraform.

This makes the architecture more explicit and reproducible.

A project like this could have a structure such as:

credit-portability-event-lab/
  apps/
    api/
    workers/
  packages/
    domain/
    events/
  infra/
    terraform/
      main.tf
      variables.tf
      outputs.tf
      modules/
        eventbridge/
        lambda/
        sqs/
        dynamodb/
        iam/
  docs/
    architecture.md
    event-catalog.md
    decisions/
  .github/
    workflows/
      ci.yml
      terraform-plan.yml
Enter fullscreen mode Exit fullscreen mode

Infrastructure ceases to be something "outside the project" and becomes part of the technical deliverable.

This also helps anyone evaluating the repository understand not only the application code but also the environment in which it runs. ---

Lab 2: Applied AI decoupled from prompts

Another topic I want to explore is AI applied to products.

Nowadays, it is relatively simple to add a call to an AI model within an application. However, integrating AI responsibly requires more than just crafting a prompt and waiting for a response.

Some important questions quickly arise:

How do you switch AI providers without rewriting the product?
How do you validate inputs and outputs?
How do you audit the context being used?
How do you prevent business logic from being hidden inside prompts?
How do you handle fallbacks?
How do you test workflows without relying on actual API calls?
How do you separate user intent from technical execution?

One possible approach is to treat AI as an architectural capability.

Instead of the product thinking directly in terms of a prompt like:

"Generate a script for a campaign video"
Enter fullscreen mode Exit fullscreen mode

it could work with a structured intent:

intent: generate_content_brief
context:
  campaign: ...
  audience: ...
  objective: ...
constraints:
  platform: instagram_reels
  format: short_video
  tone: ...
output:
  type: structured_brief 
Enter fullscreen mode Exit fullscreen mode

With this approach, the application isn't tied to free-form text or a specific vendor. Instead, it operates based on contracts.

This type of approach enables the creation of:

  • adapters for different providers;
  • mock providers for testing;
  • model fallbacks;
  • response normalization;
  • structured logs;
  • cost and latency metrics;
  • schema-based validation.

This is one of the key points I want to highlight in my projects: AI not as magic, but as software engineering.


Lab 3: Product and growth

Beyond cloud and AI, I also want to build a product-focused project: a system to organize campaigns, content, briefs, metrics, and learnings.

The idea is to start with a common pain point: many people produce content, test campaigns, and use various tools, yet the history of these activities remains fragmented. The system would feature concepts such as:

Campaign
ContentPlan
ContentObjective
Content
AiBrief
KnowledgeItem
StorySequence
Metric
Learning
Enter fullscreen mode Exit fullscreen mode

The goal would be to answer questions like:

  • which campaigns are currently underway?
  • which pieces of content need to be produced?
  • which pieces of content are overdue?
  • which objective does each piece of content serve?
  • which ideas still need to be turned into scripts?
  • which pieces of content have been published?
  • what have we learned from the results?
  • what type of content is worth repeating?

This project would focus less on heavy cloud infrastructure and more on domain modeling, product design, UX, TypeScript, Next.js, Prisma, and applied AI.

The tech stack could be:

Next.js
React
TypeScript
Prisma
PostgreSQL
Tailwind
shadcn/ui
Vitest
Enter fullscreen mode Exit fullscreen mode

It would serve as a laboratory to demonstrate the construction of a complete product without relying on complex infrastructure.


What sets these projects apart

The main difference between these projects and a traditional CRUD application lies not just in the technologies used.

It lies in the intent.

A CRUD application typically demonstrates implementation.

These projects aim to demonstrate decisions.

Decisions such as:

  • why use events?
  • where should state be persisted?
  • how should contracts be versioned?
  • what should it be synchronous or asynchronous?
  • what failures need to be anticipated?
  • how do you audit a decision?
  • how do you separate domain logic from infrastructure?
  • how do you test business rules?
  • how do you avoid vendor lock-in?
  • how do you keep the scope small without sacrificing technical value?

These decisions are closer to actual engineering work.


Documentation is also part of the portfolio

One point I want to take more seriously is documentation.

There is no point in publishing a repository with interesting code if no one can quickly understand:

  • what problem it solves;
  • how to run it locally;
  • which architecture was chosen;
  • what decisions were made;
  • what trade-offs exist;
  • what the next steps are.

Therefore, each project should have, at a minimum:

README.md
docs/architecture.md
docs/event-catalog.md
docs/decisions/
docs/roadmap.md
Enter fullscreen mode Exit fullscreen mode

I also intend to use ADRs to record important architectural decisions.

A simple ADR can answer:

  1. what the context was;
  2. what decision was made;
  3. what alternatives were considered;
  4. what consequences the decision entails.

This helps demonstrate technical reasoning, not just the final code.


CI/CD and quality

Another important point is automation.

Even for a personal project, I want to include basic validation pipelines:

lint
typecheck
test
build
terraform fmt
terraform validate
terraform plan
Enter fullscreen mode Exit fullscreen mode

This shows a commitment to quality and reproducibility.

A simple CI workflow using GitHub Actions is enough to demonstrate technical maturity.

The idea isn't to create bureaucracy, but to make it clear that the project was designed as software capable of evolving.


What I hope to learn

These projects are also a form of study.

I want to delve deeper into specific topics:

  • EventBridge in integration scenarios;
  • event catalog design;
  • modular Terraform; - serverless architecture;
  • domain tests in TypeScript;
  • secure AI integration;
  • product modeling;
  • separation of application, domain, and infrastructure;
  • minimal observability;
  • technical documentation for public projects.

The goal isn't to pretend a personal project replaces a real production environment.

It doesn't.

But a good personal project can show how a person thinks, organizes problems, and makes decisions.

And that holds great value.


How I plan to publish

The idea is to keep the projects public on GitHub, with documentation and incremental evolution.

I also intend to share lessons learned through articles and technical posts, showcasing the build process:

  • domain selection;
  • architecture design;
  • event definition;
  • infrastructure setup;
  • testing;
  • errors encountered;
  • scope adjustments;
  • decisions that changed along the way.

More than just posting the final result, I want to show the construction process.

Because, in practice, software engineering is far more about process and decision-making than it is about a finished UI screen.


Conclusion

Personal projects can be more than just CRUD exercises.

They can serve as laboratories for validating architecture, cloud, automation, product, and applied AI.

They don't need to be massive.
They don't need to try to solve the world's problems.
But they do need a clear technical intent.

In my case, I want to use these projects to demonstrate how I approach systems: starting with the domain, then moving through architecture, infrastructure, testing, automation, and documentation.

Ultimately, what I want to show isn't just that I know how to write code.

I want to show that I know how to build software with context, decision-making, and purpose.

Top comments (0)