From Individual Tools to Integrated Workflow
Welcome to our integration workshop! Throughout this week, we've explored individual components of the web development ecosystem: HTTP and web fundamentals, Git for version control, VS Code as our editor, and Docker for containerization. Now it's time to see how these pieces work together in a cohesive workflow.
Think of this workshop as assembling a vehicle. We've studied the engine (Python), the steering system (Git), the chassis (Docker), and the dashboard (VS Code) separately. Now we'll connect these components to create a functional development vehicle that can take us from concept to deployment.
By the end of this hands-on session, you'll have experienced a complete development cycle using all the tools we've learned about, preparing you for the more complex work to come in the following weeks.
Workshop Goals and Structure
In this workshop, we'll create a simple "Hello World" web application using Flask, a lightweight Python web framework. We'll implement proper project structure, version control, containerization, and deployment – all while following development best practices.
Workshop Objectives:
- Create a structured Flask application from scratch
- Implement proper Git workflow with meaningful commits
- Containerize the application with Docker
- Configure VS Code for optimal Python development
- Deploy the application locally and test it
- Document the process for future reference
Workshop Flow:
- Project planning and structure creation (20 minutes)
- Setting up the development environment (30 minutes)
- Implementing the basic application (30 minutes)
- Containerizing with Docker (30 minutes)
- Testing and debugging (20 minutes)
- Documentation and reflection (20 minutes)
This hands-on approach mimics real-world development practices, where tools and concepts don't exist in isolation but form an integrated ecosystem.
Project Planning and Structure
Before writing a single line of code, professional developers plan their project structure. This upfront investment pays dividends in maintainability and scalability.
Our Application: Flask Hello World
We'll create a simple Flask web application that:
- Serves a "Hello World" message on the homepage
- Includes a simple API endpoint returning JSON
- Has a basic HTML template using Jinja2
- Follows a scalable project structure
Project Structure
We'll use a structure that can grow with application complexity:
flask_hello_world/
├── .git/ # Git repository (created by git init)
├── .gitignore # Git ignore file
├── .vscode/ # VS Code configuration
│ ├── launch.json # Debug configuration
│ └── settings.json # Editor settings
├── app/ # Application package
│ ├── __init__.py # Package initializer and app factory
│ ├── routes.py # Route definitions
│ ├── templates/ # HTML templates
│ │ └── index.html # Homepage template
│ └── static/ # Static files (CSS, JS, images)
│ └── style.css # Basic styling
├── docker/ # Docker configuration
│ ├── Dockerfile # Main Dockerfile
│ └── docker-compose.yml # Docker Compose configuration
├── tests/ # Test directory
│ └── test_app.py # Basic tests
├── .env # Environment variables (not in Git)
├── .env.example # Example environment variables (in Git)
├── requirements.txt # Python dependencies
└── README.md # Project documentation
Why this structure matters:
This organization follows the principle of separation of concerns. Like organizing a kitchen with separate areas for prep, cooking, and plating, this structure separates different aspects of the application:
- Code that handles requests (routes.py)
- Templates that define the presentation
- Configuration files for development tools
- Documentation for developers
Real-world parallel: This structure is a simplified version of what you'd see in production Flask applications at companies like Pinterest and LinkedIn, which use Flask for certain microservices.
Setting Up the Development Environment
With our plan in place, let's create the development environment that will support our project.
Project Initialization
Let's create our project directory and initialize Git:
# Create project directory
mkdir flask_hello_world
cd flask_hello_world
# Initialize Git repository
git init
# Create basic project structure
mkdir -p app/templates app/static .vscode docker tests
touch app/__init__.py app/routes.py app/templates/index.html app/static/style.css
touch .gitignore README.md requirements.txt .env.example
touch docker/Dockerfile docker/docker-compose.yml
touch tests/test_app.py
touch .vscode/settings.json .vscode/launch.json
VS Code Configuration
Let's configure VS Code for optimal Python development by creating proper settings.json and launch.json files:
settings.json:
{
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.formatting.provider": "black",
"python.formatting.blackArgs": ["--line-length", "88"],
"editor.formatOnSave": true,
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.nosetestsEnabled": false,
"python.testing.pytestArgs": ["tests"],
"editor.rulers": [88],
"files.exclude": {
"**/__pycache__": true,
"**/.pytest_cache": true,
"**/*.pyc": true
}
}
launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Flask",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "app",
"FLASK_ENV": "development",
"FLASK_DEBUG": "1"
},
"args": [
"run",
"--no-debugger",
"--host=0.0.0.0"
],
"jinja": true
},
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
}
]
}
Git Configuration
Create a .gitignore file to avoid tracking unnecessary files:
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Virtual Environment
venv/
ENV/
# VS Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# Environment Variables
.env
.env.*
!.env.example
# Docker
.docker/
# Tests
.coverage
htmlcov/
.pytest_cache/
Python Virtual Environment
Create a virtual environment and install dependencies:
# Create a virtual environment
python -m venv venv
# Activate the virtual environment
# On Windows:
venv\Scripts\activate
# On macOS/Linux:
source venv/bin/activate
# Create requirements.txt with dependencies
cat > requirements.txt << EOL
flask==2.3.3
python-dotenv==1.0.0
pytest==7.4.0
black==23.7.0
flake8==6.1.0
EOL
# Install dependencies
pip install -r requirements.txt
Analogy: Setting up the environment is like preparing a kitchen before cooking. You ensure all utensils and ingredients are available and organized before starting to cook. Similarly, we prepare all our development tools before writing code.
Implementing the Basic Application
Now let's implement our Flask application following best practices and proper structure.
Application Factory Pattern
We'll use the application factory pattern to allow for easier testing and multiple instances:
app/__init__.py:
from flask import Flask
def create_app(test_config=None):
"""Create and configure the Flask application."""
app = Flask(__name__, instance_relative_config=True)
# Default configuration
app.config.from_mapping(
SECRET_KEY='dev',
)
if test_config is None:
# Load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# Load the test config if passed in
app.config.from_mapping(test_config)
# Register routes
from app import routes
routes.register_routes(app)
return app
# This allows running the app with 'flask run'
app = create_app()
Routes Module
app/routes.py:
from flask import render_template, jsonify
def register_routes(app):
"""Register all application routes."""
@app.route('/')
def index():
"""Render the homepage."""
return render_template('index.html', title="Flask Hello World")
@app.route('/api/hello')
def hello_api():
"""Return a JSON greeting."""
return jsonify({
'message': 'Hello, World!',
'status': 'success'
})
@app.route('/about')
def about():
"""About page."""
return render_template('index.html', title="About",
content="This is a simple Flask application created during our workshop.")
HTML Template
app/templates/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<header>
<h1>{{ title }}</h1>
<nav>
<a href="{{ url_for('index') }}">Home</a>
<a href="{{ url_for('about') }}">About</a>
</nav>
</header>
<main>
{% if content %}
<p>{{ content }}</p>
{% else %}
<h2>Welcome to our Flask Hello World application!</h2>
<p>This application was created during the Week 1 Workshop of our Python Full Stack Developer Course.</p>
<h3>API Example</h3>
<button id="apiButton">Get JSON Greeting</button>
<pre id="apiResponse"></pre>
{% endif %}
</main>
<footer>
<p>© 2025 Python Full Stack Developer Course</p>
</footer>
<script>
document.getElementById('apiButton').addEventListener('click', async () => {
const response = await fetch('/api/hello');
const data = await response.json();
document.getElementById('apiResponse').textContent = JSON.stringify(data, null, 2);
});
</script>
</body>
</html>
CSS Styling
app/static/style.css:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
header {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
nav {
margin-top: 10px;
}
nav a {
margin-right: 15px;
text-decoration: none;
color: #0066cc;
}
nav a:hover {
text-decoration: underline;
}
h1, h2, h3 {
margin-bottom: 15px;
}
main {
margin-bottom: 30px;
}
button {
background-color: #0066cc;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin: 10px 0;
}
button:hover {
background-color: #0055aa;
}
pre {
background-color: #f4f4f4;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
margin: 10px 0;
min-height: 20px;
}
footer {
margin-top: 20px;
padding-top: 10px;
border-top: 1px solid #eee;
text-align: center;
font-size: 0.8rem;
color: #666;
}
Environment Variables
.env.example:
FLASK_APP=app
FLASK_ENV=development
FLASK_DEBUG=1
Create your actual .env file (not committed to Git):
cp .env.example .env
First Git Commit
Now let's commit our initial application:
git add .
git commit -m "feat: Initial implementation of Flask Hello World application"
Real-world practice: This structured approach to implementing a Flask application reflects patterns used in production environments. The application factory pattern, for example, is used by companies like Mozilla and Twilio in their Flask applications to allow for better testing and configuration management.
Containerizing with Docker
Now that our application works locally, let's containerize it for consistent deployment.
Dockerfile
docker/Dockerfile:
FROM python:3.10-slim
WORKDIR /app
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV FLASK_APP=app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Expose port
EXPOSE 5000
# Command to run when container starts
CMD ["flask", "run", "--host=0.0.0.0"]
Docker Compose Configuration
docker/docker-compose.yml:
version: '3.8'
services:
web:
build:
context: ..
dockerfile: docker/Dockerfile
ports:
- "5000:5000"
volumes:
- ..:/app
environment:
- FLASK_APP=app
- FLASK_ENV=development
- FLASK_DEBUG=1
command: flask run --host=0.0.0.0
Building and Running with Docker
Let's build and run our container:
# Navigate to the docker directory
cd docker
# Build and start the container
docker-compose up -d --build
# Check that the container is running
docker-compose ps
Now you can access the application at http://localhost:5000
Git Commit for Docker Configuration
git add .
git commit -m "feat: Add Docker configuration for containerization"
Analogy: Containerizing an application is like vacuum-sealing a meal – it preserves all the ingredients and preparations in exactly the state you intended, ensuring it will be the same when "opened" in any environment.
Real-world application: At companies like Uber and Airbnb, containerization is a standard practice for ensuring consistency between development and production environments, especially for microservices architectures where dozens or hundreds of small applications must work together seamlessly.
Testing and Debugging
Professional developers ensure their code works correctly through testing. Let's add some tests and learn debugging techniques.
Basic Tests
tests/test_app.py:
import pytest
from app import create_app
@pytest.fixture
def app():
"""Create application for the tests."""
app = create_app({
'TESTING': True,
})
yield app
@pytest.fixture
def client(app):
"""A test client for the app."""
return app.test_client()
def test_index_page(client):
"""Test that the index page loads correctly."""
response = client.get('/')
assert response.status_code == 200
assert b'Welcome to our Flask Hello World application!' in response.data
def test_about_page(client):
"""Test that the about page loads correctly."""
response = client.get('/about')
assert response.status_code == 200
assert b'This is a simple Flask application' in response.data
def test_api_endpoint(client):
"""Test that the API endpoint returns correct JSON."""
response = client.get('/api/hello')
assert response.status_code == 200
json_data = response.get_json()
assert json_data['message'] == 'Hello, World!'
assert json_data['status'] == 'success'
Running Tests
Let's run our tests to ensure everything works:
# Run tests
pytest -v tests/
Debugging with VS Code
Now let's try debugging our application using VS Code's debug configuration that we set up earlier:
- Stop the Docker container if it's running:
docker-compose down - Open VS Code in the project directory:
code . - Set a breakpoint in routes.py, for example on the return line in the index function
- Press F5 or click the "Run and Debug" button in VS Code
- Select the "Flask" configuration
- Open http://localhost:5000 in your browser
- VS Code will pause execution at your breakpoint, allowing you to inspect variables, step through code, etc.
Common Issues and Solutions
Issue 1: Module not found errors
If you get "ModuleNotFoundError" when running the application, check:
- Ensure your PYTHONPATH includes the project root directory
- Verify that you're running the application from the correct directory
- Check import statements for typos
Issue 2: Port already in use
If port 5000 is already in use, you'll get an OSError. To resolve:
- Stop any other services using port 5000
- Change the port in docker-compose.yml and launch.json
- On Mac, stop the AirPlay Receiver which also uses port 5000
Issue 3: Docker container won't start
If the Docker container fails to start, check:
- Docker daemon is running
- No syntax errors in Dockerfile or docker-compose.yml
- Ports aren't already in use
- Check logs with
docker-compose logs
Git Commit for Tests
git add .
git commit -m "test: Add basic tests for application functionality"
Real-world practice: Companies like Shopify and Twilio have extensive test suites for their applications, with test coverage requirements before code can be merged. Debugging skills are equally valued, as they significantly reduce time spent identifying and fixing issues in production.
Documentation and Project Reflection
Documentation is critical for both personal reference and team collaboration. Let's create a comprehensive README.md file:
Project README
README.md:
# Flask Hello World
A simple Flask web application created during the Week 1 Workshop of the Python Full Stack Developer Course.
## Features
- Simple web pages with Flask routing
- API endpoint returning JSON data
- Containerized with Docker
- Configured for VS Code development
- Includes basic tests
## Development Setup
### Prerequisites
- Python 3.10 or higher
- Docker and Docker Compose
- Visual Studio Code (recommended)
### Local Setup
1. Clone the repository:
```
git clone <repository-url>
cd flask_hello_world
```
2. Create a virtual environment and install dependencies:
```
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
```
3. Create a .env file:
```
cp .env.example .env
```
4. Run the application:
```
flask run
```
5. Access the application at http://localhost:5000
### Docker Setup
1. Build and run with Docker Compose:
```
cd docker
docker-compose up -d
```
2. Access the application at http://localhost:5000
3. Stop the container when finished:
```
docker-compose down
```
## Testing
Run tests with pytest:
```
pytest -v tests/
```
## Project Structure
- `app/`: Application package
- `__init__.py`: Application factory
- `routes.py`: Route definitions
- `templates/`: HTML templates
- `static/`: Static files (CSS, JS)
- `docker/`: Docker configuration
- `tests/`: Test suite
- `.vscode/`: VS Code configuration
## What I Learned
In this project, I learned how to:
- Structure a Flask application using the application factory pattern
- Set up a development environment with VS Code
- Implement proper Git workflow with meaningful commits
- Containerize a Python application with Docker
- Write basic tests for Flask routes
- Configure debugging tools for efficient development
## Next Steps
Future enhancements could include:
- Database integration
- User authentication
- More complex API endpoints
- Frontend framework integration
- Deployment to a cloud platform
Final Git Commit
git add .
git commit -m "docs: Add comprehensive README with setup instructions and project details"
Reflection Questions
Take a few minutes to reflect on this workshop:
- How did the integration of different tools (Git, Docker, VS Code) enhance your development workflow?
- What challenges did you encounter when setting up the project structure or containerizing the application?
- How might this workflow scale to larger projects with multiple developers?
- Which parts of the setup process could be automated for future projects?
Analogy: Good documentation is like a map for a complex city. It may seem unnecessary when you know the area well, but it becomes invaluable when you return after an absence or when guiding someone new.
Real-world importance: In professional environments, documentation is often considered as important as the code itself. Companies like Google and Microsoft have strict documentation requirements, recognizing that code is read far more often than it is written, and that clear documentation reduces onboarding time for new team members.
Workshop Conclusion
Congratulations! You've successfully integrated all the key concepts from Week 1 into a functional workflow. Let's summarize what we've accomplished:
What We Built
- A structured Flask web application with routes and templates
- A functioning API endpoint returning JSON
- A containerized environment using Docker
- A configured VS Code setup with debugging support
- A Git repository with proper commits and documentation
- A test suite verifying application functionality
Key Skills Practiced
- Project planning and structure creation
- Environment configuration and dependency management
- Web application development with Flask
- Containerization with Docker
- Version control best practices
- Testing and debugging techniques
- Documentation writing
From Here to Week 2
As we move into Week 2, focusing on Python fundamentals, remember that these workflow practices apply to all types of development. The foundation we've built today – structured approach, version control, containerization, and testing – will serve as the backbone for all future projects.
Your weekend assignment will build directly on these skills, challenging you to apply them independently to create a complete containerized Python environment.
Final thought: Just as a chef doesn't just learn ingredients but also kitchen workflow, timing, and presentation, a developer needs more than just programming syntax – they need an integrated workflow that brings efficiency and reliability to their craft. Today, you've taken a significant step toward developing that professional workflow.
Additional Resources
Flask Development
- Flask Official Documentation
- Flask Mega-Tutorial by Miguel Grinberg
- Testing Flask Applications with pytest
Docker and Containerization
- Docker Getting Started Guide
- Dockerizing Flask with Postgres, Gunicorn, and Nginx
- Python-specific Docker tips
Development Workflow
Project Templates
- cookiecutter-flask - A Flask template with Bootstrap, asset bundling, and more
- flask-docker-swarm - Flask + Docker Swarm template