Introduction to Multi-Component Features

About This Unit's Practice Environment:

In previous lessons, we focused on building individual features like task comments. Those tasks were mostly local to our application — they involved a single database table and a few simple API rules. However, professional software often requires Multi-Component Features. These are features where the application must coordinate with external systems, such as cloud storage or third-party security tools.

Important: Simplified Practice Environment

The practices in this unit use simplified, mocked components designed for the CodeSignal learning environment:

  • Mock S3 storage (dictionary-based, no real AWS)
  • Simple Python classes (not full FastAPI production code)
  • Basic database simulation (no real transactions)
  • Mocked virus scanner (keyword checking, not real ClamAV)

Why this approach?

Complex production systems (real S3, Redis, PostgreSQL) can't run in browser-based learning environments. The decomposition methodology you're learning—breaking features into phases, identifying dependencies, enabling parallel work—is identical whether you're mocking services or using production infrastructure.

Your real-world application:

After this course, you'll apply these same decomposition patterns to production stacks (FastAPI + Postgres + AWS, Django + MySQL + GCP, Express + MongoDB, etc.). The thinking process is what matters, not the specific libraries.

The Task Attachments feature is a perfect example. To allow users to upload files to a task, we cannot simply save the file in a database. We need to:

  1. Store the actual file in a cloud storage bucket (like Amazon S3).
  2. Save the metadata (the file's name, size, and type) in our database.
  3. Check the file for viruses and size limits before saving it.
  4. Provide a secure way for users to download it later.

Because there are so many moving parts, we use Phased Decomposition. Instead of trying to build everything at once, we group tasks into logical phases. This keeps the AI focused and ensures that the foundation of the system is solid before we build the complex API layers on top.

Architecture: Thinking Beyond the Database

When building multi-component systems, the API acts like a traffic controller. It doesn't do all the work itself; instead, it tells other components what to do. For Task Attachments, we need a Storage Strategy. We don't want to just dump every file into one big folder. We organize them so they are easy to find and manage.

ComponentResponsibilityExample
DatabaseStores information about the file.File name: contract.pdf, Size: 2MB
Mock S3 StorageStores the actual file data.A file located at /attachments/task-123/contract.pdf
ValidatorsChecks if the file is safe and allowed.Is this file larger than 5MB?

One new concept we use here is a Presigned URL. Since we want our files to be private and secure, we don't give users a permanent link to the file. Instead, when a user asks to see an attachment, our system generates a special link that expires after 1 hour. This ensures that only authorized users can see the files.

Generating the Phased Technical Plan

Before writing any code, we must ask Claude Code to generate a Technical Plan. This plan maps out exactly how the 13 tasks will be distributed across 5 phases. We provide Claude with our specification.md and ask it to follow our decomposition principles.

You would prompt Claude like this: "Given the approved specification for task attachments, generate a technical plan for a task management system. Use simplified Python classes (not production FastAPI). We'll mock external services (S3, virus scanner) for the learning environment."

Claude will then produce a plan divided into these phases:

  • Phase 1: Foundation. Creating the Attachment database model.
  • Phase 2: Storage Infrastructure. Building the S3Client to talk to the cloud (mocked for this environment).
  • Phase 3: Validation. Building services to check file types and scan for viruses.
  • Phase 4: API Integration. Creating the endpoints where the user actually uploads the file.
  • Phase 5: Testing. Running end-to-end tests to make sure a file uploaded by a user actually reaches S3 and the database correctly.

The following diagram visualizes these phases and their dependencies:

Phase 1: Foundation
└── Database Model
    ├──> Phase 2: Storage Infrastructure
    │    └── Mock S3 Client ────┐
    │                           │
    └──> Phase 3: Validation    │
         └── File Type & ───────┤
             Virus Checks       │

                         Phase 4: API Integration
                         └── Upload/Download Controllers


                         Phase 5: Testing
                         └── End-to-End Validation

Legend:
  ──> : Dependency path (sequential)
  ──┤ : Merge point (both Phase 2 and 3 feed into Phase 4)

Notice how Phase 2 and Phase 3 both depend only on Phase 1, meaning they can be developed in parallel. This is a key optimization opportunity in the workflow.

Mapping Dependencies and Parallel Paths

In a complex 13-task project, some tasks depend on others. This is called the Critical Path. For example, you cannot build the Upload API if the Attachment Database Model doesn't exist yet.

However, many tasks are independent. We call these Parallel Opportunities. Identifying these allows a team (or you, working with Claude) to finish work faster.

Task IDComponentPhaseDependenciesCan run in parallel?
T001Attachment Model1NoneNo (Start here)
T003Mock S3Client2NoneYes (With Phase 1)
T005MIME Validator3NoneYes (With Phase 2)
T008Attachment Service4T002, T003, T005No (Needs components)

By recognizing that the Mock S3 Client (T003) and the Validators (T005T007) don't depend on the database, we can potentially work on them at the same time, reducing the total calendar time of the project from 10.5 hours to roughly 6 hours.

Executing the 13-Task Workflow

Now we begin execution. We will build the system step-by-step. First, we need a way to represent the attachment in our code.

Step 1: The Database Model

We define what information we want to save about a file.

class Attachment:
    def __init__(self, id, task_id, filename, file_size, s3_key):
        self.id = id
        self.task_id = task_id
        self.filename = filename
        self.file_size = file_size
        self.s3_key = s3_key  # The address of the file in storage

This simple class tells our application that every attachment needs a name, a size, and an s3_key (the unique ID for the file in storage).

Step 2: The Mock S3 Client

Next, we need a component that handles the actual upload. In this learning environment, we mock (simulate) the S3 service so we don't need a real internet connection to test our code.

class S3Client:
    def __init__(self):
        self.storage = {}  # Mock storage using a dictionary
    
    def upload_file(self, file_data, s3_key):
        # Simulate uploading to cloud storage
        print(f"Uploading file to: {s3_key}")
        self.storage[s3_key] = file_data
        return True

    def generate_presigned_url(self, s3_key):
        # Generate a temporary link (1-hour expiration)
        return f"https://mock-s3.example.com/{s3_key}?expires=3600"

Note: In production code, replace print() statements with proper logging (e.g., logger.info(f"Uploading file to: {s3_key}")) to enable appropriate log levels, filtering, and integration with monitoring systems.

The upload_file method handles the heavy lifting, while generate_presigned_url creates that secure, temporary link we discussed earlier.

Step 3: The Attachment Service (Integration)

Finally, we create a service that brings the database and the storage together. This is where the coordination happens.

class AttachmentService:
    def __init__(self, db_repo, s3_client):
        self.db_repo = db_repo
        self.s3_client = s3_client

    def process_upload(self, task_id, filename, file_data):
        # 1. Upload to cloud storage
        s3_key = f"tasks/{task_id}/{filename}"
        self.s3_client.upload_file(file_data, s3_key)
        
        # 2. Save metadata to database
        new_attachment = Attachment(None, task_id, filename, len(file_data), s3_key)
        return self.db_repo.save(new_attachment)

By passing the db_repo and s3_client into the service, we allow it to use both tools to complete the upload. This is a professional pattern called Dependency Injection.

Output from process_upload:
Uploading file to: tasks/101/report.pdf
Attachment saved to database with ID: 1
Summary and Next Steps

In this lesson, you have learned how to:

  1. Analyze Multi-Component Requirements: Move beyond local database changes to coordinate with external systems (or mocked versions for learning).
  2. Implement Phased Decomposition: Group tasks into logical phases to maintain AI focus.
  3. Identify Parallel Opportunities: Recognize tasks that can be developed concurrently to optimize workflow.
  4. Execute Integration Patterns: Use Dependency Injection to connect databases with cloud storage services (real or mocked).

What you're taking to production:

  • The 5-phase decomposition pattern (Foundation → Storage → Validation → API → Testing)
  • Dependency mapping techniques to identify parallel work
  • Integration patterns for coordinating multiple components
  • Rollback strategies for handling failures

You are now ready to apply these concepts in the practice exercises. You will use the CodeSignal IDE and Claude Code to build the Task Attachment system yourself, starting from a technical plan and moving through the phases of execution with simplified, mocked components.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal