Skip to content

EVERYTHING THAT WILL GO WRONG

A Solo Developer's Guide to Not Fucking Up a Distributed Telescope Network


SECTION 1: TECHNICAL FAILURES (CODE WILL BREAK)

1.1 FastAPI / Server Architecture Disasters

PROBLEM: Blocking calls in async endpoints - Symptom: Server hangs, requests timeout, everything grinds to a halt - Cause: Using time.sleep() instead of await asyncio.sleep(), synchronous database calls - Fix:

# BAD
@app.get("/targets")
async def get_targets():
    time.sleep(1)  # BLOCKS EVERYTHING
    return db.query(Target).all()  # SYNCHRONOUS QUERY

# GOOD  
@app.get("/targets")
async def get_targets():
    await asyncio.sleep(1)
    return await database.fetch_all(query)  # ASYNC QUERY

PROBLEM: Database connection pool exhaustion - Symptom: "too many connections" errors under load - Cause: Not closing connections, opening new connection per request - Fix:

# Use connection pooling
from sqlalchemy.pool import QueuePool
engine = create_engine(
    DATABASE_URL,
    poolclass=QueuePool,
    pool_size=5,
    max_overflow=10
)

# Use context managers
async def get_db():
    async with SessionLocal() as session:
        yield session  # Auto-closes

PROBLEM: Missing return statements - Symptom: 500 errors, "None is not JSON serializable" - Cause: Forgot to return anything from endpoint - Fix: Every endpoint must return something. Use type hints.

PROBLEM: Circular imports - Symptom: ImportError on startup - Cause: main.py imports router.py which imports main.py - Fix: Put shared dependencies in separate module, import carefully

PROBLEM: CORS errors from browser clients - Symptom: "No Access-Control-Allow-Origin header" in browser console - Fix:

from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Restrict in production
    allow_methods=["*"],
    allow_headers=["*"],
)

PROBLEM: Running with --reload in production - Symptom: Memory leaks, crashes, instability - Fix: Never use --reload flag in production

# DEV
uvicorn main:app --reload

# PROD
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker

PROBLEM: No error handling - Symptom: Unhelpful 500 errors, no debugging info - Fix:

from fastapi import HTTPException
from fastapi.responses import JSONResponse

@app.exception_handler(Exception)
async def global_exception_handler(request, exc):
    # Log the actual error
    logger.error(f"Unhandled exception: {exc}", exc_info=True)
    return JSONResponse(
        status_code=500,
        content={"detail": "Internal server error"}
    )

1.2 Database Disasters

PROBLEM: SQLite concurrent write failures - Symptom: "database is locked" errors - Cause: SQLite has limited concurrent write support - When it happens: As soon as you have 2+ sites submitting simultaneously - Fix: Migrate to PostgreSQL when you hit 5+ active sites

# SQLite (dev only)
DATABASE_URL = "sqlite:///./telescope.db"

# PostgreSQL (production)
DATABASE_URL = "postgresql://user:pass@localhost/telescope"

PROBLEM: No database migrations - Symptom: "column doesn't exist" errors after schema changes - Cause: Changed models.py but didn't update actual database - Fix: Use Alembic from day 1

pip install alembic
alembic init alembic
alembic revision --autogenerate -m "add priority column"
alembic upgrade head

PROBLEM: Missing indexes - Symptom: Queries get slower and slower as data grows - Fix:

class Observation(Base):
    __tablename__ = 'observations'
    # Add indexes on frequently queried columns
    target_id = Column(Integer, ForeignKey('targets.id'), index=True)
    site_id = Column(Integer, ForeignKey('sites.id'), index=True)
    timestamp = Column(DateTime, index=True)

PROBLEM: No backups - Symptom: Months of data lost to disk failure / corruption - Fix: Automated daily backups

# Add to crontab
0 2 * * * pg_dump telescope_db > /backups/telescope_$(date +\%Y\%m\%d).sql
# Keep 30 days
find /backups -name "*.sql" -mtime +30 -delete

1.3 Astronomy-Specific Code Failures

PROBLEM: Timezone disasters - Symptom: Observations timestamped wrong, targets appear at wrong times - Cause: Mixing UTC and local times - Fix: EVERYTHING IN UTC ALWAYS

from datetime import datetime, timezone

# ALWAYS use UTC
now = datetime.now(timezone.utc)

# Store in database as UTC
observation.timestamp = datetime.now(timezone.utc)

# Display to user: convert at display time only
local_time = utc_time.astimezone(site_timezone)

PROBLEM: Coordinate system confusion - Symptom: Telescopes pointing at wrong things - Cause: Mixing J2000/current epoch, degrees/hours, FK5/ICRS - Fix: Standardize on J2000 ICRS, decimal degrees for RA/Dec

from astropy.coordinates import SkyCoord
import astropy.units as u

# Store internally as decimal degrees
ra_deg = 180.0  # Not "12h 00m 00s"
dec_deg = 45.0  # Not "45° 00' 00""

# When creating coordinates, be explicit
coord = SkyCoord(ra=ra_deg*u.deg, dec=dec_deg*u.deg, frame='icrs')

PROBLEM: Altitude calculation bugs - Symptom: Scheduling targets that are below horizon - Cause: Not accounting for location correctly, wrong time - Fix:

from astropy.coordinates import EarthLocation, AltAz
from astropy.time import Time

def is_observable(target, site, min_alt=30):
    location = EarthLocation(
        lat=site.latitude * u.deg,
        lon=site.longitude * u.deg,
        height=site.altitude * u.m
    )
    now = Time.now()
    coord = SkyCoord(ra=target.ra*u.deg, dec=target.dec*u.deg)
    altaz = coord.transform_to(AltAz(obstime=now, location=location))
    return altaz.alt.deg > min_alt

PROBLEM: Sun/Moon position ignored - Symptom: Scheduling observations during twilight or near full moon - Fix:

from astropy.coordinates import get_sun, get_body
from astropy.time import Time

def is_dark(site):
    location = EarthLocation(lat=site.latitude*u.deg, lon=site.longitude*u.deg)
    now = Time.now()
    sun_altaz = get_sun(now).transform_to(AltAz(obstime=now, location=location))
    return sun_altaz.alt.deg < -18  # Astronomical twilight

1.4 Client-Side (Telescope) Failures

PROBLEM: ASCOM vs INDI fragmentation - Symptom: Client works on Windows, fails on Linux/Mac - Reality: - ASCOM = Windows only (legacy COM-based) - INDI = Linux/Mac/Raspberry Pi - ASCOM Alpaca = New cross-platform but limited adoption - Fix: Support both, or choose your audience

# Option 1: Abstract telescope control
class TelescopeInterface:
    def slew_to(self, ra, dec): raise NotImplementedError
    def get_position(self): raise NotImplementedError

class ASCOMTelescope(TelescopeInterface):
    # Windows implementation
    pass

class INDITelescope(TelescopeInterface):
    # Linux implementation
    pass

PROBLEM: Serial port conflicts - Symptom: "COM port in use" errors - Cause: Multiple programs trying to access same port - Fix: Use ASCOM's hub model, or ensure exclusive access

PROBLEM: USB disconnects - Symptom: Random observation failures, connection drops - Cause: Cheap USB cables, hub power issues, OS sleep modes - Fix: - Use powered USB hubs - Quality cables (not the free ones) - Disable USB power saving in OS - Add reconnection logic with retries

PROBLEM: Network timeouts - Symptom: Client fails to submit observations - Fix: Implement retry with exponential backoff

import requests
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, max=60))
def submit_observation(obs_data):
    response = requests.post(f"{SERVER_URL}/observations", json=obs_data)
    response.raise_for_status()
    return response.json()


SECTION 2: VOLUNTEER/RECRUITMENT FAILURES

2.1 Why People Won't Join

PROBLEM: No clear value proposition - Symptom: People say "that's cool" but don't sign up - Cause: You can't articulate why they should spend time on your project - Fix: Lead with THEIR benefit - "Get co-authorship on published papers" - "Your data helped discover a new Kuiper belt object" - "Join a community of 50+ observers worldwide" - NOT: "Help me collect data for my project"

PROBLEM: Too much barrier to entry - Symptom: People start signup, never finish - Cause: Complex installation, confusing requirements - Fix: - One-click installer (or single pip install) - 5-minute quickstart guide - Video walkthrough - Support via Discord/Slack within 24 hours

PROBLEM: Wrong audience targeting - Symptom: Getting interest from people without equipment - Fix: Target specifically: - Cloudy Nights forum (serious amateur astronomers) - AAVSO members (already doing variable star work) - Small college observatories (have equipment, need projects) - NOT: r/space (casual interest only)

2.2 Why People Will Leave

RESEARCH FINDINGS (from citizen science studies): - 60% dropout rate in first year is NORMAL - People who never submit an observation: 60% leave within months - People who submit at least one observation: 82% retention

PROBLEM: No feedback loop - Symptom: Volunteers submit data, never hear back - Cause: You're too busy coding to communicate - Fix: - Automated "thank you" email on first observation - Weekly digest: "This week the network observed 147 targets" - Monthly highlight: "Your observation of Target X was used in..." - Credit page showing all contributors

PROBLEM: Data feels useless - Symptom: "What happens to my data?" - Cause: No visibility into how data is used - Fix: - Public dashboard showing network activity - Show light curves being built - Share draft papers, acknowledge contributors - "Your data contributed to Figure 3"

PROBLEM: No community - Symptom: Volunteers feel isolated - Research: Social connection is #2 factor in retention - Fix: - Discord server (or Slack) - Monthly virtual meetups (30 min, casual) - Forum for questions - Pair up experienced/new observers

PROBLEM: Technical frustrations - Symptom: "Software doesn't work, nobody responds" - Cause: Bugs, poor documentation, slow support - Fix: - Respond to support requests within 24 hours (set expectation) - FAQ document updated continuously - Status page showing server health - Known issues page

PROBLEM: Entitlement behavior burns YOU out - Symptom: Volunteers demand features, complain about bugs - Reality: 60% of maintainers consider quitting due to toxic users - Fix: - Set expectations early: "This is volunteer-run" - Code of conduct - Don't respond to hostile messages (just close) - Remember: you can ban people

2.3 Recruitment Channels (What Actually Works)

Channel Effectiveness Notes
Cloudy Nights forum HIGH Serious amateurs
AAVSO contact HIGH Already engaged in citizen science
Reddit r/astrophotography MEDIUM Mix of casual/serious
Twitter/X astronomy MEDIUM Good for visibility
Facebook groups LOW Mostly casual
Cold emails to colleges HIGH Professors need student projects
Conference presentations HIGH In-person credibility

SECTION 3: FINANCIAL FAILURES

3.1 Hidden Costs You'll Forget

Item Monthly Cost Annual Cost Notes
VPS (Hetzner CX21) $6 $72 Minimum viable
Domain $1 $12 .org or .net
SSL $0 $0 Let's Encrypt
Email (Fastmail) $5 $60 For official comms
Backups (B2) $5 $60 100GB storage
Monitoring (free tier) $0 $0 UptimeRobot
TOTAL BASE $17 $204

PROBLEM: Scaling costs - 10 sites: Current setup fine - 100 sites: Need bigger VPS (~$20/mo) - 1000 observations/day: Need PostgreSQL optimization - 10000 observations/day: Need proper infrastructure ($100+/mo)

PROBLEM: Your time has value - 10 hours/week × 52 weeks = 520 hours/year - At $50/hr consultant rate = $26,000/year opportunity cost - Reality check: Are you okay with this being unpaid for 1-2 years?

3.2 Funding Timeline

Year 1: Bootstrap (You pay) - Total out-of-pocket: ~$200-500 - Goal: Proof of concept, 10 sites, 1 paper

Year 2: Small Grants - Apply: NSF AAG (small grants), private foundations - Realistic target: $5,000-15,000 - Covers: Server costs, travel to conferences

Year 3+: Institutional Support - Partner with university - Student labor (win-win: they get projects) - Access to grant infrastructure

PROBLEM: Grant rejection - Reality: 80%+ rejection rate for first-time applicants - Fix: - Apply to 5+ simultaneously - Get feedback, revise, resubmit - Partner with established PI


SECTION 4: PERSONAL/BURNOUT FAILURES

4.1 Solo Developer Burnout Patterns

RESEARCH FINDINGS: - 73% of developers experience burnout at some point - 60% of open source maintainers consider quitting - Solo maintainers burn out faster than teams

WARNING SIGNS: 1. Dreading opening GitHub issues 2. Not responding to messages for days 3. Avoiding the project entirely 4. Resentment toward users 5. Physical symptoms: insomnia, fatigue

PROBLEM: Scope creep - Symptom: "Could you also add..." - Cause: Saying yes to everything - Fix: - Maintain strict MVP scope - "That's a great idea for v2.0" - Public roadmap with clear phases

PROBLEM: Support burden - Symptom: Drowning in questions, bug reports - Fix: - FAQ/documentation first - Forum where users help users - Office hours (not 24/7 availability) - Template responses for common questions

PROBLEM: No boundaries - Symptom: Working on this every night, weekends - Fix: - Set schedule: "I work on this Tuesday/Thursday evenings" - Turn off notifications outside those times - It's okay for issues to sit for a week

4.2 Sustainability Patterns

What Works: 1. Time-boxing: 10 hours/week max, scheduled 2. Saying no: Feature requests go to backlog, not sprint 3. Delegation: Find 1-2 people to share load (even small tasks) 4. Celebrating wins: First observation! First paper! 10 sites! 5. Taking breaks: 1 week off every quarter

What Doesn't Work: 1. "I'll just work harder" 2. Responding to every message immediately 3. Feeling personally responsible for others' frustrations 4. Perfectionism (ship, iterate)


SECTION 5: SCIENTIFIC/CREDIBILITY FAILURES

5.1 Data Quality Problems

PROBLEM: Bad photometry - Symptom: Light curves look like noise - Causes: - Wrong comparison stars - Poor flat fielding - Focus drift - Clouds passing through - Fix: - Automated quality checks on submission - Flag outliers automatically - Require calibration frames - Quality score per observation

PROBLEM: Timing errors - Symptom: TTVs are garbage because timestamps are wrong - Causes: - Computer clock drift - No GPS time sync - Wrong timezone assumptions - Fix: - Require NTP sync or GPS timestamp - Validate timestamps on server (reject >60s offset from expected) - Cross-check with other sites observing same target

PROBLEM: Heterogeneous data - Symptom: Can't combine data from different sites - Cause: Different filters, apertures, reduction methods - Fix: - Standardize on Johnson-Cousins or Sloan filters - Require submission of comparison star magnitudes - Transform all photometry to standard system

5.2 Scientific Credibility

PROBLEM: No publications - Symptom: Nobody takes you seriously - Fix: - Write methods paper early (even with small dataset) - Submit to PASP, JATIS, or JAAVSO - ArXiv preprints count for visibility

PROBLEM: No established collaborators - Fix: - Reach out to researchers in your target science areas - Offer data access in exchange for co-authorship - Present at conferences (AAS has virtual options)

PROBLEM: Competition claims territory - Symptom: "Why wouldn't I just use ExoClock?" - Fix: Differentiate clearly - Your niche: simultaneity, occultations - Don't compete on TTVs alone (ExoClock has 326 authors) - Collaborate don't compete


SECTION 6: ORGANIZATION TOOLS (FOR YOUR SANITY)

Project Management: - Todoist (free tier): Simple task management, natural language input - Create projects: "Server MVP", "Recruitment", "Paper Writing" - Daily tasks with priorities

Documentation: - Notion (free tier): Wiki, specs, meeting notes - System architecture docs - API documentation - Volunteer onboarding guide

Code: - GitHub: Code, issues, projects board - Use GitHub Projects for kanban-style tracking - Issues for bugs and features - Milestones for releases

Communication: - Discord (free): Community, support, announcements - #general, #support, #observations, #announcements

Time Tracking: - Toggl (free): Track where your time goes - Categories: Coding, Support, Community, Admin

6.2 Daily/Weekly Rhythms

Daily (15 min): - Check Todoist for today's tasks - Glance at Discord for urgent issues - One commit (even if tiny)

Weekly (2 hours): - Review GitHub issues - Update project board - Write status update for Discord - Respond to accumulated messages

Monthly (4 hours): - Review metrics (sites, observations, retention) - Update roadmap - Write newsletter/update - Review and update documentation

6.3 Templates

Issue Response Template:

Thanks for reporting this! 

I've logged this as [issue #X]. 

Current priority: [High/Medium/Low]
Expected timeline: [This week / Next release / Backlog]

In the meantime, you can [workaround].

New Volunteer Welcome:

Welcome to [Network Name]! 🔭

Quick start:
1. Download client: [link]
2. Configure with your API key: [their key]
3. Run first test observation
4. Join Discord: [link]

Questions? Reply here or ask in #support.

Clear skies!


SECTION 7: PRE-MORTEM CHECKLIST

Before launching, ask yourself:

Technical

  • [ ] Can I explain the system architecture in 2 minutes?
  • [ ] Have I tested with 3+ simulated sites?
  • [ ] Do I have automated backups?
  • [ ] Is there a health check endpoint?
  • [ ] Have I tested failure scenarios (server restart, db down)?

Community

  • [ ] Is there a clear value proposition?
  • [ ] Can someone get started in <30 minutes?
  • [ ] Is there a support channel with <24hr response?
  • [ ] Are contributors credited visibly?
  • [ ] Do I have 3+ beta testers lined up?

Personal

  • [ ] Have I set weekly hour limits?
  • [ ] Do I have someone to share the load with (even part-time)?
  • [ ] Am I okay if this takes 2+ years to gain traction?
  • [ ] Do I have other things in my life (not just this project)?

Scientific

  • [ ] Do I have a clear first science target?
  • [ ] Is there a path to publication within 12 months?
  • [ ] Have I contacted at least one professional astronomer?

FINAL REALITY CHECK

What will definitely happen: 1. Something will break at 2am and you won't know until morning 2. Someone will submit garbage data 3. Your first 50 signups will yield 5 active participants 4. A professional will ask "why should I use this instead of X" 5. You will want to quit at least once

What makes the difference: 1. Persistence (most projects die from abandonment) 2. Community (even 5 active people changes everything) 3. One win (one paper, one discovery, one press mention) 4. Sustainable pace (10 hrs/week for 3 years > 40 hrs/week for 3 months)

The honest path: - Month 1-3: Build MVP, recruit beta testers - Month 3-6: First campaign, iterate on problems - Month 6-12: Scale to 20-50 sites, first paper submission - Year 2: Grant applications, institutional partnerships - Year 3+: Sustainable operation or graceful shutdown

You will fail at something. The question is whether you learn from it and keep going.


Last updated: January 2026 This document should be revisited monthly