rd. The SCI score provides a normalized metric for the carbon emissions of software.
SCI Formula:
SCI = (Energy * CarbonIntensity) + Embodied
- Energy: The electricity consumed by the software workload.
- CarbonIntensity: The grams of CO₂e per kWh of the grid at the time and location of execution.
- Embodied: The carbon footprint of the hardware lifecycle, allocated per unit of compute.
2. Carbon-Aware Scheduling Architecture
Decouple workload submission from workload execution. For non-real-time tasks, introduce a scheduler that queries grid carbon intensity and defers execution until the grid is cleaner or routes the job to a region with lower carbon intensity.
Technical Implementation (TypeScript):
This example demonstrates a CarbonAwareScheduler using a hypothetical CarbonAware SDK. It integrates with a job queue to evaluate whether to run immediately or defer based on a carbon threshold.
import { CarbonAwareClient, GridIntensityResponse } from '@green-software-foundation/carbon-aware-sdk';
interface Job {
id: string;
type: 'critical' | 'deferrable';
estimatedDurationMs: number;
payload: any;
}
interface SchedulerConfig {
carbonThreshold: number; // gCO2e/kWh
maxDeferralHours: number;
fallbackRegion: string;
}
export class CarbonAwareScheduler {
private carbonClient: CarbonAwareClient;
private config: SchedulerConfig;
constructor(carbonClient: CarbonAwareClient, config: SchedulerConfig) {
this.carbonClient = carbonClient;
this.config = config;
}
/**
* Determines if a job should run now or be deferred.
* Returns execution instructions including target region.
*/
async evaluateJob(job: Job): Promise<ExecutionDirective> {
// Critical jobs bypass carbon checks to preserve SLA
if (job.type === 'critical') {
return { action: 'RUN_IMMEDIATELY', region: 'current', reason: 'SLA constraint' };
}
// Fetch current grid intensity for available regions
const intensities = await this.carbonClient.getForecastForRegions({
regions: ['us-east-1', 'us-west-2', 'eu-north-1'],
durationHours: 24
});
// Identify the cleanest region within the next window
const bestRegion = this.findCleanestRegion(intensities, job.estimatedDurationMs);
if (bestRegion.intensity < this.config.carbonThreshold) {
return {
action: 'RUN_NOW',
region: bestRegion.regionId,
reason: `Intensity ${bestRegion.intensity} below threshold ${this.config.carbonThreshold}`
};
}
// Calculate next green window
const nextGreenWindow = this.findNextGreenWindow(intensities, bestRegion.regionId);
if (nextGreenWindow && this.isWithinDeferralLimit(nextGreenWindow.start, job.maxDeferralHours)) {
return {
action: 'DEFER',
region: bestRegion.regionId,
executeAt: nextGreenWindow.start,
reason: `Deferring to ${nextGreenWindow.start} for intensity ${nextGreenWindow.intensity}`
};
}
// Fallback: run in the cleanest available region even if above threshold
return {
action: 'RUN_NOW',
region: bestRegion.regionId,
reason: 'Threshold exceeded but no deferral window available'
};
}
private findCleanestRegion(forecasts: GridIntensityResponse[], durationMs: number): { regionId: string, intensity: number } {
// Logic to average intensity over the job duration for each region
// Returns region with lowest average gCO2e/kWh
return { regionId: 'eu-north-1', intensity: 45 }; // Placeholder
}
private findNextGreenWindow(forecasts: GridIntensityResponse[], regionId: string): { start: Date, intensity: number } | null {
// Logic to find the next time window where intensity drops below threshold
return null; // Placeholder
}
private isWithinDeferralLimit(executeAt: Date, maxHours: number): boolean {
const diffHours = (executeAt.getTime() - Date.now()) / (1000 * 60 * 60);
return diffHours <= maxHours;
}
}
interface ExecutionDirective {
action: 'RUN_NOW' | 'DEFER';
region: string;
reason: string;
executeAt?: Date;
}
3. Energy-Efficient Algorithmic Patterns
Carbon awareness handles the grid context; algorithmic efficiency handles the hardware load. Focus on:
- Data Transfer Minimization: Network I/O is energy-intensive. Compress payloads, use binary formats (Protobuf/MessagePack) over JSON for high-volume internal traffic, and implement aggressive caching strategies to reduce redundant compute.
- Compute Granularity: Use serverless or event-driven architectures for bursty workloads to avoid idle resource consumption. Containers should be right-sized; over-provisioned CPU limits lead to inefficient scheduling and wasted energy.
- Algorithmic Complexity: Analyze the energy cost of Big O. An O(n²) operation on large datasets not only increases latency but keeps the CPU active longer, consuming more energy. Profile memory allocation; garbage collection pauses in managed runtimes consume energy without performing work.
Architecture Decisions
- Region Selection: Prioritize regions with high renewable energy penetration (e.g., Nordic regions, specific US states with hydro/wind). Use infrastructure-as-code to tag deployments with carbon footprint estimates.
- Storage Tiering: Move cold data to archival storage immediately. Active storage consumes energy for IOPS; archival storage spins down disks, reducing energy draw significantly.
- CDN Utilization: Serve static assets via CDNs located close to the user. This reduces the distance data travels and offloads compute from origin servers.
Pitfall Guide
1. Ignoring Embodied Carbon
Focusing solely on operational energy while neglecting hardware lifecycle emissions leads to suboptimal decisions. Buying new, slightly more efficient hardware every year may increase total carbon footprint due to manufacturing emissions.
- Best Practice: Extend hardware lifecycles where possible. When provisioning, choose instances with higher utilization efficiency to amortize embodied carbon over more workloads.
2. The Rebound Effect in Software
Optimizing code for energy efficiency can lead to increased usage, negating savings. For example, making AI inference cheaper may cause teams to run inferences continuously instead of on-demand.
- Best Practice: Implement usage quotas and cost/carbon budgets per team. Monitor total energy consumption, not just intensity per request.
3. Carbon API Latency Overhead
Querying carbon intensity APIs on every request can introduce latency and consume additional energy, defeating the purpose.
- Best Practice: Cache carbon intensity forecasts. Update local cache every 5-15 minutes. Use the SDK's built-in caching mechanisms. Never block a critical path on a carbon API call.
4. Misinterpreting "100% Renewable" PPAs
Cloud providers often claim 100% renewable energy via Power Purchase Agreements (PPAs). However, PPAs are often annual and financial, not temporal. The grid may still be carbon-intensive at the moment of execution.
- Best Practice: Use temporal matching data where available. Do not assume zero-carbon execution based on provider marketing. Rely on real-time grid intensity data for carbon-aware scheduling.
5. Over-Optimizing Micro-Benchmarks
Developers may spend weeks optimizing a function that runs in milliseconds, ignoring a background job that runs for hours.
- Best Practice: Profile energy consumption, not just CPU time. Use tools like
scaphandre or carbonalyser to identify the top energy-consuming workloads. Focus optimization efforts on the "hot path" of energy usage.
6. Neglecting Data Gravity
Moving data consumes energy. Replicating datasets across regions for "resilience" without business justification creates unnecessary carbon debt.
- Best Practice: Audit data replication policies. Only replicate data where latency or compliance requirements demand it. Use lifecycle policies to delete unused datasets.
7. Greenwashing Metrics
Reporting carbon reductions based on static averages rather than actual grid intensity changes can lead to inaccurate reporting and compliance risks.
- Best Practice: Align reporting with the SCI standard. Disclose methodology, data sources, and assumptions. Use third-party verification for sustainability claims.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Real-time User API | Optimize for latency; minimal carbon checks | User experience is paramount; deferral unacceptable. | Neutral |
| Batch ETL / Data Processing | Carbon-aware scheduling with deferral | Workloads are time-flexible; grid intensity varies significantly. | High reduction |
| ML Model Training | Spot instances + Green region routing | Training is fault-tolerant and energy-intensive; spot instances often align with surplus green energy. | Significant reduction |
| Static Asset Delivery | CDN with aggressive caching | Reduces origin compute and network distance; high efficiency. | Low |
| Development Environments | Auto-shutdown policies + Local emulation | Dev environments often run idle; shutting down saves embodied and operational carbon. | Medium reduction |
Configuration Template
Kubernetes Deployment with Carbon-Aware Topology Spread
This template demonstrates how to use node labels (injected by a carbon-aware operator) to schedule pods in greener regions or nodes.
apiVersion: apps/v1
kind: Deployment
metadata:
name: carbon-aware-batch-processor
labels:
app: batch-processor
workload-type: deferrable
spec:
replicas: 10
selector:
matchLabels:
app: batch-processor
template:
metadata:
labels:
app: batch-processor
workload-type: deferrable
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/region
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: batch-processor
# Prefer regions with lower carbon intensity
# This requires a custom scheduler or node labeler that updates
# 'carbon.intensity' labels based on grid data.
nodeSelector:
carbon.intensity: "low" # Custom label managed by carbon operator
containers:
- name: processor
image: myregistry/batch-processor:latest
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
env:
- name: CARBON_THRESHOLD
value: "200" # gCO2e/kWh
- name: DEFERRAL_ENABLED
value: "true"
Quick Start Guide
- Install SDK: Run
npm install @green-software-foundation/carbon-aware-sdk in your scheduler service repository.
- Configure API: Obtain an API key from a provider like WattTime or Electricity Maps. Set
CARBON_API_KEY and CARBON_API_ENDPOINT environment variables.
- Wrap Job Handler: Modify your job execution logic to call
scheduler.evaluateJob(job) before dispatching. Implement the DEFER action by re-queuing the job with a delay.
- Deploy & Monitor: Deploy the updated service. Use your observability stack to track
sci_score and carbon_deferral_rate metrics. Verify that deferrable jobs are shifting execution times or regions as expected.