(Python_backup tool) What Adding a 'Download' Feature to the Backup Tool Taught Us About Direction-Agnostic Design
Overview
We got a request to add a restore feature to our in-house "local -> network storage" backup tool — specifically, the reverse direction: downloading files from "network storage -> local." It looked like a simple one-off addition at first, but it ended up being an opportunity to revisit the existing design.
What We Considered
There were broadly two ways to bolt on the new feature:
- Write a brand-new copy routine dedicated to downloading (fully separate from the backup logic)
- Reuse the existing copy logic as-is, and just flip the direction by swapping which argument is the source and which is the destination
The first approach is faster to implement right away, but it creates a maintenance burden down the road: any bug fix or improvement to the copy logic would need to be applied identically in two places.
The Decision and Why
Fortunately, our existing file/folder copy function was already written to operate on just two values — a source path and a destination path — with no notion of "backup" or "restore" baked into it anywhere internally. In other words, the function itself was already designed to be direction-agnostic.
common_copy(source, destination, conflict_policy):
if source is a folder:
recursively call common_copy on each child item
if source is a file:
determine the final destination path based on conflict_policy, then copy in chunks
run_backup():
common_copy(local_path, network_path, policy)
run_download():
common_copy(network_path, local_path, policy)
So instead of writing a new download-specific copy function, we decided to just call the existing one with the arguments swapped. We did split off two things to be handled separately for backup vs. download, though:
- Progress message types: we distinguished completion/error events as
Backup complete/Backup errorvs.Download complete/Download error, so the popup text shown to the user matches the situation. - In-progress flag: we shared a single "operation in progress" flag between backup and download so the two could never run at the same time.
We also reused the existing communication pattern between the worker thread and the UI (the main thread periodically polling a task queue) exactly as it was used in the original backup feature.
Retrospective
- We really felt the value of asking, when first designing a function, "might we need the reverse direction of this someday?" — it can dramatically cut the cost of adding that feature later.
- That said, this kind of generalization worked well here mainly because the existing logic "happened" to already be direction-agnostic, not because we planned for it ahead of time. We want to keep that distinct from over-abstracting preemptively just in case.