Production Readiness & Template Finalization
Objective
Finalize the template for production use with comprehensive documentation, scaffolding tools, and deployment configuration.
Background
Transform the working MVP into a reusable template that developers can clone and customize for their own projects.
Tasks
-
Create template scaffolding system -
Add production Docker configurations -
Set up CI/CD pipeline templates -
Write comprehensive documentation -
Create example application -
Add deployment guides (Heroku, Digital Ocean, AWS) -
Performance optimizations -
Security hardening -
Template customization tools
Template Scaffolding System
scripts/
├── scaffold.js # Main scaffolding tool
├── templates/
│ ├── project-name/ # Project name replacements
│ ├── database-schema/ # Custom schema templates
│ └── component-library/ # UI component templates
├── questions.js # Interactive setup questions
└── README-template.md # Generated README
Scaffolding Tool
// scripts/scaffold.js
const inquirer = require('inquirer');
const fs = require('fs-extra');
const path = require('path');
const questions = [
{
type: 'input',
name: 'projectName',
message: 'What is your project name?',
validate: input => input.length > 0
},
{
type: 'input',
name: 'description',
message: 'Project description:'
},
{
type: 'confirm',
name: 'includeMobile',
message: 'Include React Native mobile app?',
default: true
},
{
type: 'list',
name: 'database',
message: 'Choose database schema:',
choices: ['Task Management', 'E-commerce', 'Social Media', 'Custom']
}
];
async function scaffold() {
const answers = await inquirer.prompt(questions);
// Copy template files with replacements
await copyTemplate(answers);
// Generate custom schema if needed
if (answers.database === 'Custom') {
await generateSchema(answers);
}
console.log(`✅ ${answers.projectName} created successfully\!`);
console.log(`Next steps: cd ${answers.projectName} && npm run setup`);
}
Production Docker Configuration
# Dockerfile.production
FROM node:18-alpine AS builder
# Install OPAM and OCaml
RUN apk add --no-cache \
build-base \
opam \
m4
# Set up OPAM
RUN opam init --disable-sandboxing -y
RUN opam switch create 5.3.0
RUN eval $(opam env)
# Install Melange and dependencies
COPY . /app
WORKDIR /app
RUN opam install --deps-only .
RUN opam exec -- dune build @melange
# Production stage
FROM nginx:alpine AS production
COPY --from=builder /app/_build/default/packages/web/src/*.js /usr/share/nginx/html/
COPY ./nginx.prod.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
CI/CD Pipeline Template
# .gitlab-ci-template.yml
stages:
- build
- test
- security
- deploy
variables:
POSTGRES_DB: ${PROJECT_NAME}
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
build:
stage: build
script:
- opam install --deps-only .
- dune build @melange
- npm run build:web
- npm run build:mobile
artifacts:
paths:
- _build/
- packages/web/dist/
expire_in: 1 hour
test:
stage: test
services:
- postgres:15-alpine
script:
- dune build @test
- npm run test:integration
- npm run test:e2e
coverage: '/Coverage: \d+\.\d+%/'
security-scan:
stage: security
script:
- npm audit --audit-level high
- opam exec -- dune exec -- semgrep --config=auto packages/
allow_failure: true
deploy-staging:
stage: deploy
script:
- docker build -f Dockerfile.production -t ${PROJECT_NAME}:${CI_COMMIT_SHA} .
- docker push ${REGISTRY}/${PROJECT_NAME}:${CI_COMMIT_SHA}
only:
- develop
deploy-production:
stage: deploy
script:
- docker build -f Dockerfile.production -t ${PROJECT_NAME}:${CI_COMMIT_SHA} .
- docker push ${REGISTRY}/${PROJECT_NAME}:${CI_COMMIT_SHA}
- kubectl apply -f k8s/
only:
- main
when: manual
Documentation Structure
docs/
├── getting-started.md # Quick start guide
├── architecture.md # System architecture
├── database-design.md # Database patterns
├── graphql-api.md # API documentation
├── frontend-development.md # React/ReasonML guides
├── mobile-development.md # React Native guides
├── deployment/
│ ├── heroku.md
│ ├── digital-ocean.md
│ ├── aws.md
│ └── kubernetes.md
├── customization/
│ ├── adding-features.md
│ ├── styling-guide.md
│ └── database-migrations.md
└── troubleshooting.md
Example Application
(* examples/task-manager/src/components/DashboardExample.re *)
(* Comprehensive example showing all features *)
module GetDashboardData = [%graphql {|
query GetDashboard {
currentUser {
id
name
teams {
id
name
projects {
id
name
tasks(first: 5) {
id
title
status
priority
}
}
}
}
}
|}];
module Styles = {
let dashboard = [%styled.div {|
display: grid;
grid-template-columns: 250px 1fr;
height: 100vh;
background: var(--background);
|}];
let sidebar = [%styled.aside {|
background: var(--surface);
border-right: 1px solid var(--border);
padding: 1.5rem;
|}];
let main = [%styled.main {|
padding: 2rem;
overflow-y: auto;
|}];
};
[@react.component]
let make = () => {
let (result, _refetch) = useQuery(GetDashboardData.make());
<Styles.dashboard>
<Styles.sidebar>
{result
|> RemoteData.fold(
() => <div> {React.string("Loading...")} </div>,
_error => <div> {React.string("Error loading sidebar")} </div>,
data =>
<Navigation
teams={data##currentUser##teams}
currentUser={data##currentUser}
/>
)}
</Styles.sidebar>
<Styles.main>
{result
|> RemoteData.fold(
() => <LoadingSpinner />,
error => <ErrorMessage error />,
data => <ProjectOverview projects={data##currentUser##teams |> Array.flatMap(team => team##projects)} />
)}
</Styles.main>
</Styles.dashboard>
};
Performance Optimizations
(* Performance optimization examples *)
(* Memoized components *)
module TaskItem = {
[@react.component]
let make = React.memo((~task, ~onUpdate) => {
let handleClick = useCallback1(() => onUpdate(task.id), [|task.id|]);
<div onClick=handleClick>
{React.string(task.title)}
</div>
});
};
(* Optimized GraphQL queries *)
module GetTasksPaginated = [%graphql {|
query GetTasks($first: Int\!, $after: String) {
tasks(first: $first, after: $after) {
edges {
node {
id
title
status
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
|}];
Security Hardening
-- Additional security policies
CREATE POLICY "Users can only see their own data"
ON app_public.users
FOR ALL
TO app_user
USING (id = app_public.current_user_id());
-- Rate limiting function
CREATE OR REPLACE FUNCTION app_private.rate_limit(
identifier TEXT,
max_requests INTEGER DEFAULT 100,
window_seconds INTEGER DEFAULT 3600
) RETURNS BOOLEAN AS $$
DECLARE
current_count INTEGER;
BEGIN
-- Implementation of rate limiting logic
RETURN current_count < max_requests;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
Deployment Guides
# Heroku Deployment
1. Install Heroku CLI
2. Configure buildpacks:
```bash
heroku buildpacks:add https://github.com/ocaml/heroku-buildpack-ocaml
heroku buildpacks:add heroku/nodejs
- Set environment variables:
heroku config:set JWT_SECRET=your-secret heroku config:set DATABASE_URL=postgres://...
- Deploy:
git push heroku main
## Package.json Scripts
```json
{
"scripts": {
"setup": "node scripts/scaffold.js",
"dev": "concurrently \"dune build @melange --watch\" \"npm run dev:api\" \"npm run dev:web\"",
"build": "dune build @melange && npm run build:web",
"test": "dune runtest && npm run test:integration",
"deploy": "npm run build && docker build -f Dockerfile.production .",
"docs:serve": "mdbook serve docs/",
"security:check": "npm audit && opam exec -- semgrep --config=auto ."
}
}
Acceptance Criteria
-
Scaffolding tool creates working project -
All deployment options documented and tested -
CI/CD pipeline template works -
Example application demonstrates all features -
Performance benchmarks meet targets -
Security scan passes -
Documentation is comprehensive -
Template can be customized easily
🟡 Medium
Priority: Essential for template usability.
Estimated Effort: 4-5 days
Dependencies
- All previous issues completed
- Example application working end-to-end
CI Validation
-
Scaffolding tool works correctly -
Generated projects build successfully -
All deployment methods tested -
Documentation builds and serves -
Security scans pass