Building a Subscription Business as a Developer


Introduction





Subscription-based business models generate predictable recurring revenue and are the dominant monetization strategy for SaaS products. As a developer, your technical skills give you a significant advantage in building, measuring, and optimizing a subscription business. This guide covers the essential components from pricing strategy to billing implementation and metric tracking.





Pricing Tier Design





Effective pricing tiers balance value capture with customer acquisition:






# Pricing strategy framework


tiers:


free:


monthly_price: 0


features:


- Up to 100 API calls/day


- 7-day data retention


- Community support


limitations:


- No custom domains


- Rate limit: 10 req/min


goal: "Acquisition and onboarding"


pro:


monthly_price: 29


features:


- Up to 10,000 API calls/day


- 90-day data retention


- Email support (24h response)


- Custom domains


limitations: []


goal: "Primary revenue driver"


team:


monthly_price: 99


features:


- Up to 100,000 API calls/day


- 1-year data retention


- Priority support (4h response)


- Team accounts (up to 5 seats)


- API analytics dashboard


limitations: []


goal: "Team adoption and expansion"


enterprise:


monthly_price: null # Custom pricing


features:


- Unlimited API calls


- Unlimited retention


- Dedicated support engineer


- SSO/SAML


- Custom SLA


- On-premise option


limitations: []


goal: "High-value accounts"







Pricing psychology tips:


* **Decoy effect**: offer three tiers where the middle one is your target

* **Annual discount**: 20-30% discount for annual billing improves cash flow and reduces churn

* **Usage-based caps**: set fair usage limits that encourage upgrades




Stripe Billing Integration






// Stripe subscription management


import Stripe from 'stripe';




const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {


apiVersion: '2025-09-01',


});




// Create a checkout session for subscription


async function createCheckoutSession(


customerId: string,


priceId: string,


successUrl: string,


cancelUrl: string


) {


const session = await stripe.checkout.sessions.create({


customer: customerId,


mode: 'subscription',


line_items: [{


price: priceId,


quantity: 1,


}],


subscription_data: {


metadata: {


source: 'direct_checkout',


plan_tier: priceId === PRICE_PRO_MONTHLY ? 'pro' : 'team',


},


trial_period_days: 14,


},


success_url: successUrl,


cancel_url: cancelUrl,


});




return session.url;


}




// Handle subscription lifecycle via webhooks


async function handleSubscriptionWebhook(event: Stripe.Event) {


switch (event.type) {


case 'customer.subscription.created':


await onSubscriptionCreated(event.data.object);


break;


case 'customer.subscription.updated':


await onSubscriptionUpdated(event.data.object);


break;


case 'customer.subscription.deleted':


await onSubscriptionCancelled(event.data.object);


break;


case 'invoice.payment_failed':


await onPaymentFailed(event.data.object);


break;


case 'invoice.payment_succeeded':


await onPaymentSucceeded(event.data.object);


break;


}


}




async function onPaymentFailed(invoice: Stripe.Invoice) {


// Send dunning emails


if (invoice.attempt_count === 1) {


await sendEmail({


to: invoice.customer_email,


subject: "Payment failed - please update your billing info",


template: "payment_failed_first_attempt",


});


}




// Notify via Slack for manual follow-up


if (invoice.attempt_count >= 3) {


await notifySlack(


`Payment failed 3 times for ${invoice.customer_email}. ` +


`Subscription may be downgraded soon.`


);


}


}







Churn Reduction Strategies





Track churn with event analytics and implement proactive retention:






// Churn prediction and intervention


interface ChurnRiskFactors {


daysSinceLastLogin: number;


apiCallDeclinePercent: number;


supportTicketsOpen: number;


paymentFailures: number;


}




function calculateChurnRisk(factors: ChurnRiskFactors): 'low' | 'medium' | 'high' {


let score = 0;




// Login frequency


if (factors.daysSinceLastLogin > 14) score += 2;


if (factors.daysSinceLastLogin > 30) score += 3;




// Usage decline


if (factors.apiCallDeclinePercent > 50) score += 2;




// Support issues


if (factors.supportTicketsOpen > 2) score += 2;




// Billing problems


if (factors.paymentFailures > 0) score += 3;




if (score >= 5) return 'high';


if (score >= 3) return 'medium';


return 'low';


}




async function applyChurnIntervention(userId: string, risk: ChurnRiskFactors) {


// Schedule automated outreach


if (risk.daysSinceLastLogin > 7) {


await sendEmail({


userId,


template: "we_miss_you",


delay: risk.daysSinceLastLogin > 14 ? 0 : 24 * 60 * 60 * 1000,


});


}




// Offer downgrade path before cancel


if (risk.paymentFailures > 0) {


await offerDowngradeTier(userId);


}


}







Customer Lifecycle





Map the customer journey from acquisition to expansion:






Acquisition → Activation → Retention → Revenue → Referral


| | | | |


Landing First Monthly Upgrade Share with


page value check-in prompts friends







Automate each stage:






async function handleCustomerLifecycle(userId: string, daysSinceSignup: number) {


switch (true) {


case daysSinceSignup === 0:


// Send welcome email with getting started guide


await sendWelcomeSequence(userId);


break;


case daysSinceSignup === 3:


// Check if they've used key features


const usage = await getUserUsage(userId);


if (usage.apiCalls === 0) {


await sendOnboardingReminder(userId);


}


break;


case daysSinceSignup === 14:


// Trial ending soon


await sendTrialEndingEmail(userId);


break;


case daysSinceSignup === 30:


// One-month review: ask for feedback


await sendNpsSurvey(userId);


break;


case daysSinceSignup % 90 === 0:


// Quarterly check-in


if (isPowerUser(userId)) {


await suggestUpgrade(userId);


}


break;


}


}







Key SaaS Metrics





| Metric | Formula | Target | Why It Matters |


|---|---|---|---|


| MRR | Monthly recurring revenue | Growing 10-20% month-over-month | Core health metric |


| ARR | MRR x 12 | >$100K for seed stage | Annual run rate |


| LTV | ARPU / Churn Rate | >3x CAC | Customer lifetime value |


| CAC | Sales + Marketing / New Customers | <$500 for self-serve | Cost to acquire |


| NDR | Net Dollar Retention | >100% | Expansion minus contraction |


| Churn Rate | Cancelled / Total Customers | <5% monthly | Revenue retention |





Track these with a simple dashboard:






-- Monthly recurring revenue calculation


SELECT


DATE_TRUNC('month', subscription_start) AS month,


COUNT(*) AS customers,


SUM(monthly_price) AS mrr,


SUM(monthly_price) * 12 AS arr


FROM subscriptions


WHERE status = 'active'


GROUP BY 1


ORDER BY 1 DESC;




-- Churn rate


SELECT


DATE_TRUNC('month', cancelled_at) AS month,


COUNT(*)::float /


(SELECT COUNT(*) FROM subscriptions


WHERE status = 'active'


AND subscription_start < DATE_TRUNC('month', s.cancelled_at))


AS churn_rate


FROM subscriptions s


WHERE status = 'cancelled'


GROUP BY 1


ORDER BY 1 DESC;







Start with a simple pricing model (one paid tier), launch with Stripe's pre-built checkout, and focus on the first $1K MRR before optimizing pricing or building complex billing logic.