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)¶
6.1 Recommended Stack (All Free Tiers)¶
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