Astranis · Software Engineer
Updated · 2026-10-02

Astranis Software Engineer
Interview Guide

THE 60-SECOND BRIEF

Astranis builds small, high-throughput geostationary communications satellites (micro-GEO) for commercial and defense customers. Candidates describe software as part of the spacecraft itself: it drives power subsystems, avionics, thermal control and software-defined radios, rather than sitting on top of someone else's hardware. A Software Engineer can land on one of several tracks: C++ flight software, FPGA signal-processing pipelines, cloud-based mission control and telemetry tools, or Python and full-stack tooling for hardware-in-the-loop (HITL) manufacturing and test. Day to day, reports describe working beside avionics, thermal and mechanical engineers and ground operations staff.

This guide is for candidates interviewing for a Software Engineer role at Astranis, whether the target is the embedded and flight track (C++ or C) or the test, production and ground-tools track (Python, with web frameworks such as React and TypeScript in some roles). It covers the three reported stages, all twenty reported questions grouped by subject, and a seven-day plan. It does not cover pay or offer negotiation.

Candidates report three stages over roughly 3-5 weeks: a Recruiter Call, a Technical Screen on a shared coding or whiteboard platform, and a Full Interview Loop made of several focused technical modules.

Embedded SystemsPythonFull-Stack Development

44 min read

Browse Software Engineer questions

See the practice prompts

24Practice promptsAcross five skill areas

The reported work splits along a hardware boundary. On the flight side it is C++ (and C) running on avionics and payload processors, where the requirements candidates describe include an RTOS, bare-metal or embedded Linux experience, and bus protocols such as I2C, SPI, CAN, UART and Ethernet. On the test and ground side it is Python for component- and system-level production tools, HITL simulation harnesses, telemetry pipelines and web control panels. Fault detection, isolation and recovery (FDIR) state machines appear on both sides. Decide which track you are interviewing for before you start, because the reported language choice follows the track.

The twenty reported questions fall into four groups of five. Embedded and flight software covers a thread-safe circular buffer, RTOS task priorities, bit masking of telemetry packets, avoiding heap allocation in C++, and debugging memory corruption without a debugger. State machine and systems architecture covers a payload mode machine, sensor-disconnect handling, concurrent ground commands, a ground-station telemetry ingest pipeline and recovery after a processor reset. Hardware-software interfacing and physics reasoning covers solar panel thermal balance, I2C and SPI timing, ADC noise, power-rail failure detection and closed-loop antenna pointing. Automated testing and production infrastructure covers a thermal vacuum chamber script, a HITL harness, fault injection for FDIR, CI on embedded targets and asynchronous Python instrument control.

Reported loops weight Embedded Systems, Python, Full-Stack Development, Automated Testing and Mechanical Fundamentals (first-principles physics). That mix suggests two kinds of practice. First, write real code from a blank editor: a lock-free or mutex-guarded buffer, a bitfield decoder, a table-driven state machine, an asyncio instrument loop. Second, rehearse estimating physical quantities out loud with units and stated assumptions, such as the equilibrium temperature of a sun-facing panel.

Candidates also report that interviewers change a problem's requirements midway, and that state machine sessions add edge cases such as telemetry lost mid-transition or conflicting ground commands after the first version works. Build your solutions so they can absorb that: define safe and error states first, keep transitions in a table, and keep hardware access behind a narrow interface you can mock.

01

Recruiter Call

reported

Candidates report an opening conversation with a technical recruiter, sometimes an engineering manager, that reviews your background, project experience and fit with the role. Treat it as a short technical story rather than a formality: the loop that follows is built around software that touches physical hardware, so your most relevant project should be ready to describe in plain terms.

What to demonstrate

  • How your background and project experience map to the track you are applying for
  • Whether your hardware-adjacent experience is concrete enough to discuss
  • Fit with the role and the company's satellite work

How to prepare

  • Choose one project where your code controlled a device, sensor, robot or test bench, and rehearse a two-minute version covering what broke and how you found it.
  • State clearly whether you are targeting flight C++ or Python test and ground tools, and match your project choice to that track.
  • Check the posting for the citizenship or permanent-resident requirement that candidates report for flight hardware, ITAR-controlled and defense roles, and raise it early if you are unsure.
Astranis Software Engineer candidate reports ↗
02

Technical Screen

reported

Candidates report a technical session of about 45 minutes to an hour, held over video on a shared-code platform such as Coderbyte or a virtual whiteboard, with one or two such screens in some loops. Reports describe live coding, system architecture or first-principles analysis. Because this is the stage where the coding-style questions (the circular buffer, bit masking, memory allocation in C++) are most naturally practised, rehearse them in a plain editor.

What to demonstrate

  • Working code produced live in a shared editor
  • Whether you clarify the problem and state assumptions before typing
  • First-principles reasoning when the numbers or specification are incomplete

How to prepare

  • Write the thread-safe circular buffer and the bitfield packet decoder from a blank file, without autocomplete, and run each against a few hand-made inputs.
  • Practise saying the assumptions aloud first: capacity, producer and consumer count, byte order, what happens when the buffer is full.
  • Do one back-of-envelope physical estimate, such as panel equilibrium temperature, with units written at every step.
Astranis Software Engineer candidate reports ↗
03

Full Interview Loop

reported

Candidates report a series of focused technical modules with engineers from software, avionics and hardware teams, covering software architecture, low-level coding and collaboration. Reports say state machine sessions start from a set of requirements, ask for an implementation or diagram, then add an edge case, and that other follow-ups change requirements part-way through. Expect to move between code, whiteboard diagrams and conversation with people who do not share your specialty.

What to demonstrate

  • Software architecture for flight and ground systems
  • Low-level coding judgement around memory, concurrency and hardware interfaces
  • Collaboration and communication across software, avionics and hardware
  • Adapting a design when requirements change mid-session

How to prepare

  • Build the payload mode state machine in C++ with a table of transitions and named safe and fault states, then add sensor-disconnect handling without rewriting it.
  • Sketch the telemetry ingest pipeline, the HITL harness and the fault-injection framework as component diagrams you can redraw from memory and extend on request.
  • Practise explaining a trade-off (heap versus static pools, polling versus interrupts) in terms an avionics or thermal engineer can check.
Astranis Software Engineer candidate reports ↗

PracHub editorial advice for the preparation topics above.

01

Writing transition logic for the payload state machine before naming the safe and fault states

List every mode, the safe state each one falls back to, and what a corrupted or missing input does, then write the transition table. Candidates report that edge cases such as mid-transition telemetry loss arrive after the first version works, so a table-driven design lets you add a row instead of restructuring.

02

Reaching for std::vector, new, std::function or a blocking mutex in code that runs in a flight path or interrupt context

Use a fixed-capacity buffer, static storage or a pool allocator, and say why: heap fragmentation, unbounded latency and priority inversion. For the circular buffer and the allocation question, name what happens when the buffer is full and which operations may run in an ISR.

03

Answering the thermal or bus-timing question with a formula or register name but no stated assumptions or units

Write the energy balance in words first (absorbed power in, radiated power out, thermal mass for the transient), list assumptions such as absorptivity, emissivity and eclipse, and carry units through. For I2C and SPI, state clock rate, what the software delay does to the bus, and how a timeout and retry behave.

04

Treating one ADC sample or one missed packet as truth when designing power-rail or sensor-fault logic

Describe a filter and its delay, a debounce count, hysteresis on thresholds and a plausibility check before any state change. Say how long a failing rail may be tolerated before isolation, and how the isolation is reversed.

05

Restarting from scratch when the interviewer changes a requirement mid-problem

Keep hardware access behind a small interface and logic in data tables, then say what changes and what stays. Candidates report requirement changes during the loop, so rehearse absorbing a new constraint out loud with a friend instead of silently rewriting.

Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.

21 technical prompts0 include a worked solution

Implement a thread-safe cyclic circular buffer in C++ for streaming high-frequency telemetry data from an onbo

medium
Embedded & Flight Software Engineering

Implement a thread-safe cyclic circular buffer in C++ for streaming high-frequency telemetry data from an onboard sensor.

Approach
  1. Back it with a fixed std::array<T, N> plus head and tail indices, so nothing allocates after construction. With N a power of two the index is count & (N - 1). Use either a separate count or one always-empty slot so that full and empty look different.
  2. For one producer and one consumer, make it lock-free with std::atomic<size_t> head and tail. The producer writes the slot and then stores tail with memory_order_release. The consumer loads tail with memory_order_acquire, reads the slot and stores head with release. Each index then has exactly one writer.
  3. With several producers, or a consumer that needs to block, guard push and pop with a std::mutex and a condition_variable. When the producer is an interrupt handler, no mutex can be used: keep it SPSC or briefly mask the interrupt around the critical section.
  4. Choose a policy for a full buffer and say it out loud. Dropping the newest sample and incrementing an overrun counter keeps the SPSC ownership rule. Overwriting the oldest means the producer has to move head, so it needs a lock or a different design.
  5. Push and pop are O(1) and allocation-free. Put head and tail on separate cache lines with alignas(64) to avoid false sharing, and restrict T to trivially copyable types so copying a slot cannot throw.
Follow-up
  • How does your design change if the producer is a sensor interrupt and the consumer is an RTOS task?
  • How would you show it is race-free: a stress test, ThreadSanitizer, or both?
  • Can you overwrite the oldest sample on overflow without adding a lock?

What mechanisms would you use in modern C++ to prevent dynamic memory allocation overhead during active flight

medium
Embedded & Flight Software Engineering

What mechanisms would you use in modern C++ to prevent dynamic memory allocation overhead during active flight maneuvers?

Approach
  1. Allocate everything during initialisation and forbid the heap afterwards. One way is to replace global operator new and malloc (for example with -Wl,--wrap=malloc) so they trap or log once an init-complete flag is set, then check the map file for any remaining heap users.
  2. Use fixed-capacity storage: std::array, fixed-capacity containers such as those in the Embedded Template Library, or a std::vector reserved at init (it does not reallocate until size exceeds capacity). Construct objects in static buffers with placement new.
  3. Use pool or fixed-block allocators backed by a free list: O(1) allocate and free, deterministic timing and no fragmentation. Alternatively use std::pmr::monotonic_buffer_resource over a static buffer with std::pmr::null_memory_resource() upstream, so overflow fails loudly instead of falling back to the heap.
  4. Know where allocations hide: std::function with large captures, std::string beyond the small-string buffer, std::shared_ptr control blocks, thrown exception objects, and iostreams. Replace them with templates or a function_ref-style callable, std::string_view, and error codes or -fno-exceptions.
  5. Virtual functions do not allocate; their cost is an indirect call and a vtable pointer. If that matters in a tight loop, CRTP gives static dispatch. Measure before trading away clarity.
Follow-up
  • How would you prove in CI that nothing allocates after initialisation?
  • What should the code do when a pool runs out mid-manoeuvre?
  • Why can heap fragmentation fail a long-running process even when total free memory looks sufficient?

How do you handle concurrency and race conditions when multiple ground commands attempt to update state machin

medium
State Machine & Systems Architecture

How do you handle concurrency and race conditions when multiple ground commands attempt to update state machine parameters simultaneously?

Approach
  1. Serialise commands. Put every incoming command on one queue consumed by a single task that owns the state machine and its parameters. No other task writes that state, so the data race disappears by design rather than by locking.
  2. Apply multi-parameter updates atomically. Validate the whole set, then publish it with a double buffer and an atomic pointer or index swap, or copy it in under a short mutex. Readers then see either the old set or the new set, never a mix.
  3. Give each command a sequence number so duplicates and replays can be rejected. For conflicting edits, have the command carry the parameter version it was based on and reject it if that version has moved on (optimistic concurrency).
  4. Return an explicit ack or nack with a reason for every command. Let a safe-mode command jump the queue or preempt, and reject parameter changes that are invalid in the current state.
  5. If locks are unavoidable: hold them briefly, never across I/O, take them in one global order to avoid deadlock, and use priority inheritance.
Follow-up
  • Two operators send conflicting parameter changes seconds apart. What does each of them see?
  • A command arrives in the middle of a state transition. Do you queue it, reject it or apply it?
  • How would you test for race conditions here?

Days 2 to 6 each pick one group of reported questions and end with working code or a written design; day 1 maps the loop and day 7 is a mock with a changed requirement and the behavioral stories.

Small steps. Visible outcomes.0 / 7 completed
ONE WEEK · YOUR PACE

Prepare, practise & reflect

One practical outcome each day. Spend longer where you need it.

0 / 7 done
01Map the loop and choose a track
  • Write the three reported stages and note which question types each format naturally covers: coding in the technical screen, architecture, low-level coding and collaboration in the loop.
  • Sort the twenty reported questions into four groups: embedded and flight C++, state machines and architecture, hardware interfacing and physics, test and production infrastructure.
  • Decide whether you are preparing the flight C++ track or the Python test and ground track, and list your two weakest groups.

Deliverable: A one-page loop map, the four question groups with your confidence in each, and your chosen track.

02C++ memory, buffers and RTOS priorities
  • Implement a fixed-capacity circular buffer for telemetry samples that is safe for one producer and one consumer, with std::atomic indices and no heap allocation, and add a mutex-based version for multiple producers.
  • List the C++ mechanisms that avoid heap use during flight: static storage, std::array, pool allocators, std::pmr over a static buffer, and what you avoid (exceptions, std::function, virtual calls on the hot path).
  • Write a one-page RTOS priority table for guidance, attitude control, telemetry and housekeeping tasks, covering priority inheritance and deferring ISR work to tasks.

Deliverable: A compiled, tested buffer source file, a written list of allocation-free mechanisms, and a one-page priority table with reasons.

Practice prompt ↗Practice prompt ↗Practice prompt ↗
03Bit-level decoding and debugging without a debugger
  • Write a decoder for a made-up serial telemetry packet with a header bitfield, a multi-byte field, a checksum and both byte orders, using masks and shifts only, and add unit tests for malformed frames.
  • Write a triage sequence for memory corruption on a target with no debugger: guard bytes and canaries, stack high-water marks, pool usage counters, a memory protection unit if present, and reproducing on a host build with sanitizers.

Deliverable: A decoder with passing tests and a numbered corruption-triage checklist.

Practice prompt ↗Practice prompt ↗
04State machines and reset recovery
  • Implement the payload mode machine (launch, deployment, payload active, safe) in C++ as a transition table, with the safe and fault states defined before any transition.
  • Extend it to handle a sensor disconnect and corrupted state telemetry, and add a test for each added path.
  • Design how ground commands that change state-machine parameters are serialised: one command queue that owns the state, with validation and a rejected-command response.
  • Write a short note on recovery after a processor reset: what is persisted with a checksum, what the boot path reads, and how the machine re-enters a safe mode.

Deliverable: A compiled state machine with transition tests and a written reset-recovery and command-ordering note.

Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗
05Physics, buses and sensors
  • Write a function that estimates a sun-facing solar panel's temperature over time from absorbed power, radiated power and thermal mass, and state your assumptions and units.
  • Make a one-page I2C versus SPI sheet: signalling, addressing, clock stretching, acknowledgement, and what software delays or a missing timeout do to a transaction.
  • Sketch software for ADC noise and filter delay, and for a power-rail monitor with debounce and hysteresis that isolates a failing rail.
  • Outline a closed-loop pointing controller (sensor, estimator, PID, actuator limits) that keeps running while momentum is being dumped.

Deliverable: A tested temperature function, a bus cheat sheet, and two short written designs for rail monitoring and pointing control.

Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗
06Test infrastructure and ground pipeline
  • Write an asyncio script that drives two simulated instruments through a timed sequence, with locks, per-query timeouts, a bounded queue and a safe-state cleanup on failure.
  • Sketch a HITL harness that mocks hardware inputs, plus a fault-injection framework that triggers FDIR logic in the state machine from day 4 and checks the expected recovery.
  • Outline a CI pipeline that runs unit tests on a host build and integration suites on target hardware, and say how a shared board is reserved.
  • Draw the ground-station ingest pipeline for thousands of telemetry parameters per second: decode, validate, buffer, store, and what happens on out-of-order or missing packets.

Deliverable: A runnable instrument-control script with fake instruments and three diagrams (HITL and fault injection, CI, telemetry ingest).

Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗
07Mock screen with a changing requirement, plus stories
  • Run a timed mock in a plain shared editor on the payload state machine, and have a partner add the sensor-disconnect requirement and a conflicting-command case part-way through while you narrate assumptions aloud.
  • Write stories for hardware-adjacent teamwork: a bug that was not in your code, a requirement change you absorbed, and a disagreement with a hardware owner.
  • Use PracHub's practice questions on arguing against a design and on defending an estimate to rehearse those two stories under time.

Deliverable: A recorded or written mock run with notes on where you stalled, plus three finished stories.

Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗

Expand any day for tasks and deliverables. Your progress is saved on this device.

Candidates report that the Recruiter Call covers your background and fit, and that the Full Interview Loop includes a collaboration module alongside the technical ones, with engineers from software, avionics and hardware teams. For this role that means stories about software meeting physical devices and about working across disciplines. Only the first prompt below is a reported question (and it is technical in substance); the rest are rehearsal prompts built from the reported themes.

How do you handle asynchronous data streams in Python when controlling multiple lab instruments simultaneously

medium
Automated Testing & Production Infrastru

How do you handle asynchronous data streams in Python when controlling multiple lab instruments simultaneously?

Approach
  1. Classify the work first: talking to bench instruments over SCPI on TCP, VISA or serial is I/O-bound, so asyncio fits. Use one task per instrument that owns its connection, and put an asyncio.Lock around every write-then-read pair so two coroutines never interleave commands on the same link.
  2. Most instrument drivers (PyVISA, pyserial) block. Wrap each blocking call in asyncio.to_thread, or run_in_executor on a dedicated pool, so one slow query does not freeze the event loop. If a vendor library is not thread-safe, give it a single-thread executor so calls are serialised.
  3. Move data through bounded asyncio.Queue objects: instrument tasks put timestamped samples (monotonic clock for ordering, wall clock for logs) and one consumer writes to disk. The bound gives backpressure; decide the overflow policy up front (block, drop oldest, or abort the test) and log every drop.
  4. Put a timeout on every query, retry a bounded number of times, and run the tasks inside asyncio.TaskGroup (Python 3.11+) so one failed instrument cancels its siblings. Never swallow CancelledError, and use a finally block to put the chamber and supplies into a safe state.
  5. State the trade-off: threads suit blocking drivers but need locks around shared state, while multiprocessing is for CPU-heavy analysis, not instrument I/O. Test with fake instruments that inject delays, malformed replies and disconnects so the failure paths actually run.
Follow-up
  • One instrument stops replying halfway through a long sequence. What happens to the other instruments and to the chamber?
  • How would you unit-test this without any lab hardware attached?
  • Why asyncio rather than one thread per instrument, and what would make you switch?

Argue against a design, lose, and commit anyway

medium
disagreementservice boundariesdecision records

Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.

Approach
  1. State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
  2. Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
  3. Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
  4. Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
Follow-up
  • What threshold on that alert would have proved you right, and did anyone ever look at it?
  • If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?

Estimate work you have never done and defend the range

hard
estimationbackfillsexpand-contract

You are asked to estimate a change you have never attempted: add a column to a 100-million-row table, populate it, move reads across, and drop the old shape. Give a range with the assumptions that generate it, including batch size, the signal your backfill throttles on, and wall-clock hours, and name the three unknowns that would move the number most. Then describe a real estimate you gave under comparable ignorance: how you expressed its uncertainty, what you committed to, and how wrong you turned out to be.

Approach
  1. Decompose into independently deployable steps before estimating anything: add the column nullable, write both shapes, backfill in batches, verify, move reads, stop writing the old shape, drop it. That is four deploys spread over days, and the calendar estimate is dominated by them rather than by the loop's runtime.
  2. Do the arithmetic aloud for the part that has arithmetic in it: batch size times number of batches times per-batch duration, at a write rate the primary can absorb alongside roughly 1.2k writes per second of production traffic. The loop is throttled by replication lag and lock waits, not by how fast it can issue statements.
  3. Price the schema step by its lock rather than its statement duration. In PostgreSQL an ALTER TABLE taking ACCESS EXCLUSIVE waits for every open transaction on that table while later queries queue behind it, so a millisecond change issued during a thirty-second analytics query stalls that table for thirty seconds. Adding a nullable column with a non-volatile default avoids a rewrite from version 11; a new index wants CREATE INDEX CONCURRENTLY, which cannot run inside a transaction block and leaves an invalid index behind if it fails.
  4. Express the answer as a range whose endpoints each trace to a stated assumption, then name the cheapest experiment that collapses it, which is almost always running one real batch against the real table and multiplying.
Follow-up
  • How do you verify the backfill genuinely finished, given rows written by production traffic while it ran?
  • Where does the backfill resume from after a worker is killed mid-batch, and what makes that resume point trustworthy?
  • 01

    How do you handle asynchronous data streams in Python when controlling multiple lab instruments simultaneously?

  • 02

    Tell me about a time your software controlled a physical device, and a failure turned out not to be in your code. How did you show where it was?

  • 03

    Describe a time a requirement changed while you were mid-implementation. What did you keep, what did you rework, and how did you tell the people depending on you?

  • 04

    Walk me through a disagreement with a hardware, electrical or test engineer about where a fault came from, and how you settled it with evidence.

  • 05

    Why this company and this track, and which project of yours best shows you can work close to hardware?

PracHub preparation framework ↗
How hard are the Astranis technical interviews compared with typical software loops?

Candidates describe a different kind of difficulty rather than a harder version of the usual one: the emphasis reported is on systems engineering, state machine logic, low-level execution and physics reasoning, not abstract dynamic-programming puzzles. Prepare by writing embedded-style code (buffers, bit masking, state machines) and by practising short first-principles estimates.

Astranis Software Engineer candidate reports ↗
Which programming language should I use?

Candidates report that C++ and C are the languages for embedded and flight roles, while Python and web stacks (TypeScript and React) come up for test infrastructure and ground systems. Candidates also report being able to use the language that suits the track, so confirm with your recruiter and then practise in that language only.

Astranis Software Engineer candidate reports ↗
Do I need aerospace experience?

Reports describe aerospace background as a plus, and list automotive, robotics, consumer electronics, defense and high-performance infrastructure among the backgrounds engineers come from. What you can control is evidence of hardware contact: a project where your code drove a sensor, motor or test bench, and a clear account of a fault you traced.

Astranis Software Engineer candidate reports ↗
How many stages are there and how long does the process take?

Candidates report three stages (Recruiter Call, Technical Screen, Full Interview Loop) over about 3-5 weeks, with recruiter outreach often within one to two weeks of applying. The screen is reported on a shared-code platform such as Coderbyte or a virtual whiteboard, so practise typing correct code without an IDE.

Astranis Software Engineer candidate reports ↗
Is there a citizenship requirement?

Candidates report that roles involving flight hardware, ITAR-controlled technical data or defense programs require US citizenship or permanent residency. Read the specific posting and ask the recruiter during the first call if you are unsure, so it does not surface after you have invested weeks in preparation.

PracHub Software Engineer practice ↗
How much physics and electronics should I review?

Review enough to set up and sanity-check a model: the heat balance for a body in sunlight (absorbed power, radiated power, thermal mass), Ohm's law, RC filter delay and unit conversions. Practise turning each into a few lines of code, since reported questions ask for a function or a calculation rather than a lecture.

PracHub Software Engineer practice ↗
Sources & methodology 3 sources ↗

No official company page is cited. Rounds and questions come from candidate reports and PracHub editorial material; each source shows the date it was read.