How followup-cadence.mjs Calculates Optimal Timing for Application Follow-ups
The followup-cadence.mjs script calculates optimal timing by applying configurable business rules to each application's status, days since submission, and follow-up history, returning deterministic dates through pure functions like computeNextFollowupDate() and urgency levels via computeUrgency().
The followup-cadence.mjs module in the santifer/career-ops repository serves as the core engine for the follow-up cadence tracker. It determines exactly when you should contact employers next and flags how urgent those follow-ups are. By parsing markdown trackers and applying deterministic logic based on the CADENCE configuration, it removes guesswork from calculating optimal timing for application follow-ups.
The Three-Stage Calculation Pipeline
The script processes data through a logical pipeline that transforms raw tracker files into actionable scheduling recommendations.
Stage 1: Data Normalization and Parsing
First, the script ingests and standardizes input data. It reads data/applications.md and data/follow-ups.md, then converts raw status strings to canonical forms like applied, responded, and interview.
parseTracker()(lines 85–101) extracts application dates, company names, and current statuses from the applications tracker.parseFollowups()(lines 105–127) processes the follow-up history log to determinefollowupCountandlastFollowupDate.normalizeStatus()(lines 59–63) maps various input strings to standardized state values for consistent downstream processing.
Stage 2: Urgency and Date Computation
For each actionable entry, the script calculates three key metrics: days since application (daysSinceApp), days since last follow-up (daysSinceLastFollowup), and number of previous follow-ups (followupCount). These metrics feed into two pure functions:
computeUrgency()(lines 55–73) returns one of four priority levels:urgent,overdue,waiting, orcold.computeNextFollowupDate()(lines 75–91) returns the specific calendar date for the optimal next contact, ornullif the application has gone cold.
Stage 3: Ranking and Output Generation
Finally, entries are sorted by urgency (from urgent to cold) according to the sorting logic (lines 60–63). The script generates either a JSON payload (lines 68–80) or a human-readable dashboard containing the suggested next date, days until that date, and the calculated urgency level. Users can filter results using flags like --overdue-only to focus on immediate priorities.
Cadence Configuration and Business Rules
All timing decisions derive from the CADENCE constant defined at lines 32–40. This configuration object centralizes the business logic for calculating optimal timing:
const CADENCE = {
applied_first: 7, // days after first application before first follow-up
applied_subsequent: 7, // days between successive follow-ups while "applied"
applied_max_followups:2, // stop after two follow-ups – entry becomes "cold"
responded_initial: 1, // urgent if response received within 1 day
responded_subsequent: 3, // overdue after 3 days without new follow-up
interview_thankyou: 1, // thank-you after interview should be sent next day
};
Runtime customization is available via CLI arguments. For example, --applied-days <n> (line 30) overrides applied_first to adjust the initial waiting period without modifying source code.
Computing the Next Follow-Up Date
The computeNextFollowupDate() function implements status-specific logic to determine the optimal contact date. It uses addDays() (lines 79–83) to perform calendar calculations, adding specified day intervals to ISO-8601 dates.
Applied Status Logic
For applications with status applied, the timing depends on follow-up history:
if (status === 'applied') {
if (followupCount >= CADENCE.applied_max_followups) return null; // "cold"
if (followupCount === 0) return addDays(parseDate(appDate), CADENCE.applied_first);
// After the first follow-up
return addDays(parseDate(lastFollowupDate), CADENCE.applied_subsequent);
}
If no follow-ups exist, the script schedules the first contact applied_first days after the application date. Subsequent follow-ups occur every applied_subsequent days. After reaching applied_max_followups, the function returns null, indicating the lead has gone cold.
Responded Status Logic
When an employer responds, the urgency increases. The script calculates the next follow-up based on when the response occurred:
if (status === 'responded') {
if (lastFollowupDate) return addDays(parseDate(lastFollowupDate), CADENCE.responded_subsequent);
return addDays(parseDate(appDate), CADENCE.responded_subsequent);
}
If the user has already followed up on the response, it schedules the next contact responded_subsequent days after that last follow-up. Otherwise, it calculates from the original application date.
Interview Status Logic
For interview stages, the timing focuses on post-interview etiquette:
if (status === 'interview') {
return addDays(parseDate(appDate), CADENCE.interview_thankyou);
}
The function returns a date exactly interview_thankyou days after the interview, ensuring prompt thank-you communication.
Urgency Classification Logic
The computeUrgency() function evaluates the same temporal inputs to surface priority levels. It categorizes each application to help users triage their job search:
- Applied entries become
overduewhen the first-follow-up window or subsequent window is exceeded, otherwisewaitingorcold. - Responded entries trigger
urgentif the first response is fresh (less thanresponded_initialdays old), andoverdueafter theresponded_subsequentthreshold passes. - Interview entries show
overdueonce the thank-you window (interview_thankyou) has passed.
This classification drives the ordering in the final JSON output and the color-coded terminal dashboard (lines 102–124).
Usage Examples
Programmatic API
You can import the calculation functions directly to integrate optimal timing logic into custom workflows:
import {
computeNextFollowupDate,
computeUrgency,
parseDate,
} from './followup-cadence.mjs';
// Example: Application submitted on 2024-03-01, still "applied", no prior follow-up
const status = 'applied';
const appDate = '2024-03-01';
const lastFollowupDate = null;
const followupCount = 0;
// Determine the optimal next contact date
const next = computeNextFollowupDate(status, appDate, lastFollowupDate, followupCount);
console.log('Next follow-up should be on:', next); // → 2024-03-08 (default 7 days)
// Determine urgency (assume today is 2024-03-09)
const today = new Date('2024-03-09');
const daysSinceApp = Math.floor((today - parseDate(appDate)) / (1000*60*60*24));
const urgency = computeUrgency(status, daysSinceApp, null, followupCount);
console.log('Urgency level:', urgency); // → "overdue"
Command-Line Execution
Run the full analysis from your terminal to process tracker files and generate reports:
# JSON output (default)
node followup-cadence.mjs > cadence.json
# Human-readable dashboard
node followup-cadence.mjs --summary
# Show only overdue items
node followup-cadence.mjs --overdue-only
The CLI reads data/applications.md and data/follow-ups.md, applies the cadence rules, and prints either structured JSON or a formatted table based on the output mode selected.
Summary
followup-cadence.mjscalculates optimal timing through a three-stage pipeline: data parsing, metric computation, and urgency-based ranking.- Timing logic is driven by the
CADENCEconfiguration object (lines 32–40), which defines intervals for first follow-ups, subsequent contacts, and maximum outreach attempts. - Pure functions
computeNextFollowupDate()andcomputeUrgency()return deterministic dates and priority levels based on application status, elapsed days, and follow-up count. - Status-specific rules handle
applied,responded, andinterviewstages differently, ensuring context-appropriate scheduling. - Flexible output supports both JSON API consumption and human-readable dashboards via CLI flags.
Frequently Asked Questions
How does the script handle applications where I've already followed up multiple times?
The computeNextFollowupDate() function tracks followupCount and compares it against CADENCE.applied_max_followups. If you have reached the configured maximum (default is 2), the function returns null and the entry is classified as cold, indicating you should stop following up and move on to other opportunities.
Can I customize the number of days before my first follow-up?
Yes. You can override the applied_first value at runtime using the --applied-days <n> CLI flag (line 30), or modify the CADENCE constant directly in the source code at lines 32–40. This allows you to adjust your approach from the default 7-day waiting period to match industry-specific expectations or personal preference.
Why does the script calculate different intervals for "responded" versus "applied" statuses?
The CADENCE configuration recognizes that employer responses require faster reaction times. While applied status uses a 7-day cycle, responded status triggers an urgent classification within 1 day (responded_initial) and marks items overdue after 3 days (responded_subsequent). This reflects the higher priority of maintaining momentum when an employer shows active interest compared to cold applications.
What file formats does the script expect for input data?
The script expects markdown tracker files located at data/applications.md and data/follow-ups.md. The parseTracker() function (lines 85–101) and parseFollowups() function (lines 105–127) parse these files to extract dates, company names, contact information, and status history, converting them into JavaScript objects for processing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →