PolicyEngine is a multi-repository organization that builds microsimulation models for tax and benefit systems across multiple countries. The codebase is organized into several main components:
- policyengine-core: Fork of OpenFisca-Core that powers country models and apps
- policyengine-api: Flask-based API serving policy simulations and calculations
- policyengine-app: React-based web application (the main user interface)
- policyengine-us: US federal and state tax/benefit microsimulation model
- policyengine-uk: UK tax-benefit microsimulation model
- policyengine-canada: Canadian tax-benefit model
- policyengine-il: Israel tax-benefit model
- policyengine-ng: Nigeria tax-benefit model
- policyengine-us-data: Enhanced CPS microdata for US simulations
- microdf: Python package for analyzing distributional effects
- survey-enhance: Tools for imputation and reweighting of survey data
- policyengine.py: Python package for programmatic PolicyEngine access
Multiple specialized calculators and dashboards for specific policy analyses (e.g., salt-amt-calculator, child-tax-credit-expansion-impact, 2024-election-dashboard)
- Python Version: 3.10-3.12 (check each project's pyproject.toml for specifics)
- Installation:
make installorpip install -e ".[dev]" --config-settings editable_mode=compat - Virtual Environment: Recommended to use venv or conda for isolation
- Node Version: >=22.0.0 (use nvm for version management)
- Installation:
npm ci(notnpm installto ensure lockfile consistency) - Package Manager: npm (not yarn or pnpm)
make install # Install dependencies
make test # Run all tests
make format # Format code with Black (79 chars)
make changelog # Update changelog before PR
pytest path/to/test.py::test_function -v # Run specific testmake install # Install npm packages and Python tools
make debug # Start dev server
make test # Run Jest tests
make format # ESLint fix + Prettier
npm run lint -- --max-warnings=0 # Check CI will passmake debug # Start Flask dev server
make debug-test # Run tests with debug output
make deploy # Deploy to Google Cloud- Formatter: Black with 79-character line length
- Imports: Grouped by stdlib, third-party, local (alphabetized within groups)
- Naming: snake_case for functions/variables, CamelCase for classes
- Type Hints: Use where possible, especially for public APIs
- Docstrings: Required for all public functions/classes
- Error Handling: Catch specific exceptions, provide helpful messages
- Formatter: Prettier (default config) + ESLint
- Components: Functional with hooks (no class components)
- React Imports: Use named imports (
import { useState } from "react") - File Naming: PascalCase for components, camelCase for utilities
- Component Size: Keep under 150 lines after formatting
- TypeScript: Gradually adopting - use for new files when possible
policyengine-{country}/
├── policyengine_{country}/
│ ├── parameters/ # YAML files with tax rates, thresholds
│ │ └── gov/ # Organized by department/agency
│ ├── variables/ # Python formulas for calculations
│ │ └── gov/ # Matches parameter structure
│ ├── reforms/ # Pre-defined policy reforms
│ ├── tests/ # Test cases for validation
│ ├── entities.py # Person, household definitions
│ └── model_api.py # Main API entry point
├── Makefile
├── pyproject.toml
└── changelog.yaml
- Lowercase, concise labels
- Include legislative references
- Organized by government department
- Use correct department (e.g., education → DfE, not DWP)
- One variable per file (recent refactoring standard)
- Include regulatory citations in comments
- Import from policyengine_core.variables import Variable
- Use numpy functions (np.ceil, not ceil)
- Write tests for all new functionality
- Use pytest framework
- Place in tests/ directory matching source structure
- Run with coverage to ensure adequate testing
- For country models: YAML test cases for calculations
- Jest + React Testing Library
- Tests in
__tests__directories with.test.jssuffix - Test user interactions, not implementation details
- Mock API calls appropriately
- Coverage reports in
coverage/directory
- Add
changelog_entry.yamlat repo root:- bump: patch|minor|major changes: added|changed|removed|fixed: - Description of change
- Run
make changelogto update main changelog - Commit both changelog files
- Fork the repository
- Create feature branch from master/main
- Keep fork synced:
git pull upstream master - Open PR from feature branch to upstream master
- Include "Fixes #123" in PR description
- Request review when ready
- Clear, descriptive messages
- Reference issue numbers
- Explain "why" not just "what"
- Flask-based REST API
- Redis for caching
- Google Cloud SQL for persistence
- Background jobs with RQ (Redis Queue)
- Country models loaded dynamically
- User Input → React App
- API Request → Flask API with country/reform/household data
- Simulation → Country model (OpenFisca-based)
- Results → Cached and returned to frontend
- Visualization → Plotly charts in React
/calculate: Run household calculations/economy/{country_id}/over/{policy_id}: Society-wide impacts/parameters: Get parameter metadata/variables: Get variable metadata
- State Management: No global state - lift state up
- Routing: React Router v6 with search params
- Country Switching: Use useCountryId hook
- API Calls: Use fetch with proper error handling
- Charts: Plotly.js with react-plotly.js wrapper
- Variables: Inherit from Variable class
- Parameters: Defined in YAML, accessed via parameter system
- Reforms: Modify parameters or add/replace variables
- Entities: Person, household, tax unit hierarchies
- Time Periods: Handle different period types (year, month, eternity)
- App Engine: Hosts API and React app
- Cloud SQL: PostgreSQL for data persistence
- Cloud Build: CI/CD pipeline
- Service Accounts: For deployment authentication
- Development:
.envfiles (not committed) - Production: Set in GCP App Engine config
- API keys: Stored securely, never in code
- Use
pytest -vvfor verbose test output - Add
import pdb; pdb.set_trace()for breakpoints - Check variable values with simulation.calculate()
- Use
--durations=0to find slow tests
- React Developer Tools browser extension
- Console.log liberally during development
- Check Network tab for API calls
- Use React Profiler for performance
- Import Errors: Check PYTHONPATH and editable installs
- CORS Issues: Verify API URL configuration
- State Updates: Use spread operator for immutability
- Enum Values: Must match exactly (case-sensitive)
- Run formatters before committing
- Write self-documenting code
- Add comments for complex logic
- Keep functions focused and small
- Handle edge cases explicitly
- Cache expensive calculations
- Use React.memo for pure components
- Lazy load large components
- Optimize Plotly chart rendering
- Profile before optimizing
- Never commit secrets or API keys
- Validate all user inputs
- Use parameterized queries
- Follow OWASP guidelines
- Regular dependency updates
- Semantic HTML elements
- ARIA labels for complex widgets
- Keyboard navigation support
- Color contrast compliance
- Screen reader testing
- Each repo's README.md for specific setup
- API documentation at /api/documentation
- Country model docs in docs/ directories
- GitHub Issues for bugs/features
- Internal Slack/Discord for questions
- Code reviews for learning patterns
- Pair programming encouraged
# All Python projects
make install # Install dependencies
make test # Run tests
make format # Format code
make changelog # Update changelog
# React app specific
make debug # Start dev server
make build # Production build
# API specific
make debug-test # Debug mode tests
make deploy # Deploy to GCP- Python:
snake_case.py - React Components:
PascalCase.jsx - Tests:
test_*.pyor*.test.js - YAML:
snake_case.yaml
- Parameters:
parameters/gov/{dept}/{policy}.yaml - Variables:
variables/gov/{dept}/{concept}.py - React Pages:
src/pages/{PageName}.jsx - API Routes:
policyengine_api/endpoints/