Lessons Learned from Real-World Azure + Bitbucket Deployments
Designing CI/CD pipelines feels straightforward…
until your builds become inconsistent, deployments start failing, and environment management turns messy.
After working extensively with:
- Bitbucket Pipelines
- Azure App Service (Linux)
- Node.js backends
- React / Angular frontends
- Monorepos
…I’ve gathered practical lessons that saved hours of debugging and prevented production issues.
This post focuses on real-world patterns, not textbook theory.
🧱 1️⃣ Deterministic Builds Are Non-Negotiable
One of the most common CI/CD mistakes:
npm install
❌ Why this is risky in pipelines
- Can modify package-lock.json
- Non-reproducible builds
- “Works locally, fails in CI”
✅ Best Practice
npm ci
For deployment bundles:
npm ci --omit=dev
✔ Benefits
- Enforces lockfile
- Faster installs
- Reproducible builds
📦 2️⃣ Don’t Ship Dev Dependencies to Production
Early pipelines often bundle everything:
node_modules/
❌ Consequences
- Bloated artifacts
- Slower deployments
- Higher memory usage
- Cold start delays
✅ Better Strategy
Reinstall only production dependencies:
npm ci --omit=dev
✔ Result
- Smaller ZIP
- Faster startup
- Cleaner runtime
⚛ 3️⃣ Frontend Configuration: The Silent Problem
For React / Angular / Vite apps:
npm run build
👉 Environment variables become hardcoded into JS bundles
❌ Symptoms
- Azure App Settings changes do nothing
- Rebuild required per environment
✅ Scalable Solution → Runtime Config
Create an env.js file:
window.__env = {
API_URL: "https://api.example.com"
};
Load before the app bundle:
<script src="/env.js"></script>
Access in React:
const apiUrl = window.__env?.API_URL;
✔ Advantages
- Build once, deploy anywhere
- No rebuild per environment
- Easy config updates
- Customer-specific deployments
🔐 4️⃣ Frontend Secrets? They Don’t Exist.
Important reality:
❌ You cannot truly hide secrets in frontend apps
Anything delivered to the browser can be inspected.
🚨 Never expose
- API keys
- DB credentials
- Client secrets
✅ Correct Architecture
Frontend → Backend → Third-party
Secrets live only in:
- Backend
- Azure App Settings
- Key Vault
🌱 5️⃣ One YAML vs Multiple YAML Files
As projects grow:
👉 Should we create separate YAML per branch?
❌ Why this becomes painful
- Pipeline drift
- Duplication
- Maintenance overhead
✅ Recommended
Use one common YAML with branch logic:
branches:
dev:
test:
main:
🔁 6️⃣ Repeated Steps? Use YAML Anchors
Avoid copy-paste pipelines.
✅ Use Anchors
definitions:
steps:
- step: &build_step
Reuse:
- step: *build_step
✔ Benefits
- Cleaner YAML
- Easier updates
- Less duplication
☁️ 7️⃣ Azure Deployment: Pipe vs CLI
Two popular approaches:
✅ Atlassian Azure Pipe (Preferred)
pipe: atlassian/azure-web-apps-deploy
✔ Why it’s great
- Minimal scripting
- Cleaner pipelines
- Fewer failure points
🧰 Azure CLI (Use When Needed)
Best for:
- Slot swaps
- Infra provisioning
- Advanced automation
❌ Avoid installing CLI manually
curl InstallAzureCLIDeb
✅ Use Azure CLI image instead
image: mcr.microsoft.com/azure-cli
🚨 8️⃣ Dangerous Commands to Avoid (Unless Justified)
Examples:
az resource update ... allow=true
PORT=8080 hardcoding
sleep 30 hacks
✅ Only acceptable when
- Legacy constraints
- Debugging scenarios
- Platform requirements
- Always document the reason.
🔐 9️⃣ Repository vs Deployment Variables
Misusing variables causes many CI/CD issues.
✅ Repository Variables
Use for:
- Shared config
- Build constants
Examples:
- NODE_VERSION
- LINT_FLAGS
✅ Deployment Variables
Use for:
- Environment-specific values
- Secrets
Examples:
- API_URL
- Azure credentials
🏆 Final Takeaways
✅ Pipelines must be deterministic
✅ Use npm ci, not npm install
✅ Install prod dependencies only
✅ Artifacts should be environment-agnostic
✅ Never store secrets in frontend
✅ Prefer runtime config for SPAs
✅ Use one YAML with branch logic
✅ Use YAML anchors to avoid duplication
✅ Prefer deployment pipes over complex scripts
✅ Use Azure CLI only when flexibility required
🏆 Conclusion: CI/CD is Easy… Until It Isn’t
Setting up a pipeline that “works” is not difficult.
But designing a pipeline that is:
- Deterministic
- Environment-agnostic
- Secure
- Scalable
Easy to maintain
👉 requires deliberate engineering decisions.
Small shortcuts like:
❌ Using npm install
❌ Embedding secrets in frontend
❌ Duplicating YAML per branch
❌ Shipping devDependencies
…may work initially but create serious friction as projects scale.
Final Thought
Your CI/CD pipeline is not just automation…
👉 It is part of your system architecture.
Treat it with the same care as application code.











