# Outside In Development
## Summary
Outside-in development is an approach to building software that starts with the user-facing functionality and works inward toward the implementation details. This methodology, often paired with Behaviour Driven Development (BDD), helps ensure that development efforts remain focused on delivering actual business value and prevents over-engineering of internal components.
## Core Principles
### Start with User Behaviour
Development begins by defining how users will interact with the system, typically through:
- User stories or features
- Acceptance criteria
- End-to-end scenarios
- Integration tests
Note that these high-level behaviour tests will typically fail until all the underlying implementation layers are complete.
### Work Inward
The implementation follows a path from external interfaces toward internal components:
1. Begin with user interface or API endpoints
2. Move to controllers or service layer
3. Progress to business logic
4. Finally implement data access and models
At each layer, use mocks or test doubles to isolate the component being tested and allow development to proceed before dependent components are fully implemented.
### Test-First Approach
Tests are written in a specific order:
1. Acceptance tests (e.g., Cucumber features)
2. Controller/service tests
3. Unit tests for business logic
4. Model/data access tests
## Benefits
Outside-in development provides several advantages:
- Ensures focus on user needs
- Prevents unnecessary code
- Maintains connection to business value
- Supports iterative development
- Reduces over-engineering
- Keeps development aligned with requirements
## Implementation Process
### 1. Start with Feature Test
Begin by writing a complete feature test that describes the desired behaviour:
- Define the user interaction
- Specify expected outcomes
- Include acceptance criteria
- Use business language
This test will fail initially and serve as a guide for the implementation.
### 2. Work Through Layers
Follow a systematic approach through the application layers:
1. Create minimal UI/endpoint
2. Implement controller logic with mocked services
3. Add service layer with mocked repositories
4. Create domain models
5. Implement data access
### 3. Test Each Layer
Write appropriate tests for each layer while implementing:
- End-to-end tests for features (will remain red until full implementation)
- Integration tests for workflows
- Unit tests for components using mocks for dependencies
- Model tests for data logic
### 4. Using Mocks and Test Doubles
At each layer:
- Mock downstream dependencies
- Focus on component responsibilities
- Test component behaviour in isolation
- Replace mocks with real implementations as you move inward
### 5. Minimal Implementation
At each stage:
- Write just enough code to pass tests
- Avoid speculative functionality
- Refactor as patterns emerge
- Keep focus on current feature
## Common Patterns
### Red-Green-Refactor Cycle
1. Write failing test (Red)
2. Implement minimum code to pass (Green)
3. Clean up implementation (Refactor)
### Mock Usage Patterns
- Mock dependencies not under test
- Use interface-based mocking
- Verify mock interactions
- Avoid mocking value objects
### Layer Integration
- Use dependency injection
- Define clear interfaces
- Maintain loose coupling
- Follow SOLID principles
### Test Organisation
Structure tests to mirror application layers:
- Feature/acceptance tests
- Integration tests
- Controller tests with mocked services
- Service tests with mocked repositories
- Model tests
## Best Practices
### Do
- Start with user stories
- Write acceptance tests first
- Work systematically inward
- Use mocks for unimplemented dependencies
- Keep implementation minimal
- Refactor regularly
### Don't
- Skip layers in testing
- Write unnecessary tests
- Implement speculative features
- Over-mock simple objects
- Lose focus on user needs
- Over-engineer solutions
## Tools and Frameworks
Common tools that support outside-in development:
- Cucumber - Feature testing
- RSpec - Unit testing
- Jest - JavaScript testing
- Cypress - End-to-end testing
- JUnit - Java testing
- Mockito - Java mocking
- Jest Mock - JavaScript mocking
- RSpec Mocks - Ruby mocking
## Example Workflow
Using a library application feature:
1. Write feature test describing user adding book (initially fails)
2. Create route for new book page
3. Implement controller action with mocked book service
4. Add view template
5. Create book service with mocked repository
6. Create book model
7. Implement repository
8. Feature test now passes
## Related Concepts
### Behaviour Driven Development (BDD)
- Focuses on system behaviour
- Uses ubiquitous language
- Encourages collaboration
- Supports outside-in approach
### Test Driven Development (TDD)
- Write tests first
- Minimal implementation
- Regular refactoring
- Clean code focus
### Domain Driven Design (DDD)
- Rich domain models
- Bounded contexts
- Ubiquitous language
- Business focus
## References
- Based on article by Sarah Mei: "Outside-In BDD: How?!"
- Growing Object-Oriented Software, Guided by Tests (GOOS)
- BDD in Action (Behavior-Driven Development for the whole software lifecycle)
- The RSpec Book: Behaviour-Driven Development with RSpec, Cucumber, and Friends