1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

git pullでデータが消えた - 保存先設計を2回変えた話 - Day 8

1
Last updated at Posted at 2026-05-09

はじめに

Day 7で書いた通り、これまではツールの基礎についての記事を書いてきました。

Week 2からは、実装中に発生した設計変更を中心に記録していきます。今回はデータ保存先の設計を変えることになった経緯と、最終的にOSのユーザーデータ領域への保存を採用した理由について書きます。


設計の変遷

データ保存先の設計は以下の2段階で変遷しました。

段階 方式 廃止理由
第1段階 リポジトリ直下にデータを配置 git pullによるデータ消失が発生したため
第2段階(現行) OSのユーザーデータ領域に保存 採用

第1段階:環境変数による保存先指定

初期設計では、データの保存先を環境変数(STUDY_CSV_DIRSTUDY_JSON_DIR)で指定していました。インストールと同時に保存先の設定も完了させることを目指した設計です。

また、この方式を採用した場合は、インストール後にユーザーが環境変数を手動で設定しなければならないという問題が発生する可能性を発見。この発見そのものが「ユーザーへの追加設定を一切負わせない」という開発方針と矛盾するため廃止しました。

データ消失事例

環境変数方式では、リポジトリ直下にデータを配置する方式を採っていました。保存したデータファイルは、親ディレクトリごと.gitignoreで管理対象から外せば問題ないと考えていましたが、ここでデータ消失事例が発生しました。

内容としては、git pullを実行した際に、.gitignoreで管理対象外にしていたCSVファイルがディレクトリごと消えてしまいました。リポジトリ直下へのデータ配置は、外部操作による干渉を避けられないという事実をこの時点で認識しました。


第2段階:OSユーザーデータ領域への移行

初期の失敗から、問題の本質を「外部操作によるデータへの干渉」と特定しました。OSが管理するユーザーデータ領域であれば以下の要件を全て満たせます。

要件 内容
ユーザーへの追加設定不要 環境変数・手動パス設定は不要
pullによるデータ消失なし GitのpullはOS管理領域に干渉しない
アップデートによるデータ消失なし バイナリ更新とデータ領域が分離される
NN機能からの統一参照 全モジュールが同一パスを参照できる

保存先は以下の通りです。

OS 保存先
Linux ~/.local/share/milestone_manager/
Windows %APPDATA%\milestone_manager\
macOS ~/Library/Application Support/milestone_manager/

platformdirsを使わず自前実装を選んだ理由

OS判定による保存先の振り分けには、platformdirsのような外部ライブラリを使わず自前で実装しています。

理由は2つあります。1つは配布時のバイナリファイルを小さく保つためです。もう1つは、platform.system()と条件分岐で実装できる規模であり、外部ライブラリを追加するほどの複雑さではないと判断したためです。

実装はfile_operations.pycreate_path関数として分離しています。

def create_path(file):
    target_dir = 'milestone_manager/'
    # os judgement
    if platform.system() == 'Linux':
        base_path = os.path.join(pathlib.Path.home(), '.local/share/')
    if platform.system() == 'Windows':
        env = os.environ.get('APPDATA')
        base_path = pathlib.Path(env)
    if platform.system() == 'Darwin':
        base_path = os.path.join(pathlib.Path.home(), 'Library/Application Support/')


    if not os.path.exists(os.path.join(base_path, target_dir)):
        os.mkdir(os.path.join(base_path, target_dir))
    file_path = pathlib.Path(os.path.join(base_path, os.path.join(target_dir, file)))

    return file_path

この関数は各OSに対応したベースパスを取得し、引数で受け取ったファイル名やディレクトリパスを結合して返す仕様です。各モジュールはこの関数を呼び出すだけで保存先を意識せずに済む設計になっています。

なお、現在の開発環境がLinuxのみのため、macOSとWindowsでの動作検証が行えていません。この点は課題として認識しており、検証環境が用意でき次第対応する予定です。


おわりに

今日はデータ保存先の設計変遷について書きました。

3段階の変遷はどれも「動作はするが要件を満たさない」という判断によるものです。設計の失敗を記録しておくことで、同じ問題を踏む方が減れば幸いです。

Day 9ではCSVフォーマットの設計について書く予定です。strftimeのディレクティブの誤りによる不正値出力と、既存ツールの調査を経てフォーマットを修正した経緯を記録します。


この記事は連載「クラウドに依存しないマイルストーン管理ツール開発記」のDay 8です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?