がるの健忘録

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

ORMについて考えてみる

ORMで初っぱなボケようとして割とよい感じの「別の意味」が見つからなくてもんどり打ってるおいちゃんでございます*1
まぁボケっぱなしだと話が続かないので、速やかに本題に。

Object-relational mapping、でございます。O/RMって書かれたりORMって書かれたり。
ここでは面倒なんでORMで。

そのORMですが。
評価については、「まんせー」から「Go to Hell」まで、割と激しい幅がある技術なんじゃないかなぁ? とか思ったりするんですがいかがでしょうか?
実は、後ほど「ORMに言及する」別のBlogを書こうとしているので、「現時点での」おいちゃんの見解を書いておきたいなぁ、と。

おいちゃんの見解としては
・軽いものはORMで書くけどやっかいなやつはSQLダイレクトに書いたほうが早い事が多いよねぇ
ってのをベースに
・チーム内に「ORMに長けたメンバーがいて厄介な質問でも回答が帰ってくる」んならORM側に寄せますがそうでなければSQL側に寄せます
って感じかなぁ、と。

んで。
おいちゃんが知っている限り。
……いやまぁ「不勉強で触った事もなくてなんとなく新しい技術は毛嫌いしているからORMヤダ」って人がいないわけではないのですが。
どちらかというと「実際に使ってみて(使いこなしたとは言っていない)、一定量のトラブル経験を経て嫌になった」人のほうが、割合的には多いんじゃないかなぁ? と。

んで。
「一定量のトラブル」については
・そもそもORMという思想では根本的に解決不能な問題(あるのかどうかは知らない:可能性として)
・ORMの実装的に根本的に解決不能な問題(あるのかどうかは知らない:可能性として:これはありそうだなぁ)
・ORMで解決できなくもないが、トリッキーだったり特殊なやり方が必要だったいする(近しいのは見た事があるなぁ)
・ORMで一応解決可能だが、実装が複雑だったり難しかったり知られてなかったり
あたりが割とあるんじゃないかなぁ? とか思ったりするです。

特に後半2つが割と厄介で。
「チーム内にORMに長けたメンバーがいて厄介な質問でも回答が帰ってくる」んならある程度許容範囲かもしれないのですが、「ORMに長けたメンバーがいなくて、ちょいちょいと調べ物が発生したり、ORM自体のソースコードに潜ったりする必要がある」とかって感じになってくると、途端にストレスの上昇カーブが激しくなってくるかなぁ、と。
さりとて、そのORMに深い思い入れがあるわけでなければ「ものすごいコストをかけてそのORMの微に入り細を穿つところまで習得する必要は、あるの?」とかいう疑問が横切った瞬間に「生SQLでよくね?」的なチョイスに向かう事も、少なくないのではなかろうか、と。

んで。
よくORMの例題に出てくるような「シンプルで単純なSQL」なら、そりゃORMのほうが楽でしょうて。
ただ、そういった「シンプルで単純なSQL」以外が、業務では、出てくる可能性が0ではなくて*2
その辺を「どうするか?」ってあたりへの回答が
・無理
・トリッキーなやり方で出来る
・出来るけど情報を探すまでが少し厄介
・普通に出来る
ってあたりで色々グラデーションがあるんじゃないかなぁ? と。

例えばよく言われるあたりが「N+1問題」。
ただまぁ、それ以外にも「なんでこんな変なわかりにくいコードかかないかんねんこれならSQL書いたほうが早いじゃん」的な、etc etc。

その辺を考えると、 https://www.kaitoy.xyz/2015/09/13/orm-is-offensive-anti-pattern/ あたりも、一笑に付す事が、幾分、難しい側面もあるんじゃないかなぁ、とか。

んで。
ちぃと興味深いBlogを見つけたので、少し言及してみたく。
https://qiita.com/niisan-tokyo/items/156eb35c6eeaf07b9b65

1. SQLを書かなくていい!

これが「利点になるかどうか」は、「やりたい事を、SQLとORMのコードで書いて比較」次第なんじゃないかなぁ? とか思ったり。

そこを前提に。

1.1 SQLという「別言語」を混在させずに済む

それをいうと、PHPを書いててもHTMLだのJavaScriptだのと「完全に無縁」とはなかなかいかないような気もしますし。

あと、個人的な所感ですが、SQLの直書きが許されている場合、似たようなSQLが量産される傾向にあるように思います。

については「環境が悪いだけじゃないかなぁ?」とか思うわけです。
あるいは「ORMになったからといって、似たようなコードが量産されないのか?」という疑問があったり。

1.2 「よい」SQLを作ってくれる

「ナニをもってよいとしているんだろう?」と思ったのですが、

これはSQLインジェクションに対する対処としては定石となる手法ですが、手で書こうとすると割と面倒だったりします。

えぇ………… orz
それは、ないと思うなぁ。

2. ゲッター・セッターの仕組みを利用できる

これは別にORM使わなくても出来る。

3. 所有関係を定義できる

「JOIN使えばいいじゃない」と言うべきなのか「FOREIGN KEY使おうよ」と言うべきなのか。

んで。

1.1 SQLを書かない
中略
SQLをバリバリ書ける人にとっては、SQLが勝手に生成されるORMは自由度がないと思うかもしれません。

まぁ、ここだよねぇ、的な。
んなにバリバリ書けるわけではないのですが、それでも割とちょいちょいと「縛りがきついんじゃ*3」と思う瞬間は、割とちょいちょい。

1.2 JOINを使わないかもしれない

……これは「ない」と信じたいなぁ……知らんが。
ちなもし「JOINを使ってない」場合、

これが絶対に悪いかというと、実はそうとも言い切れません。

と書かれてますが、基本的に「がっつりと悪い」です。

2. 嫌ってる人がいる

「なんで嫌ってるのか?」次第だよねぇ、と。

3. N+1問題

言わずもがな。

と言うわけで。

エンジニアにとって、コード量を少なく可読性をよくできることが、ソフトウェアの品質を上げる有効な手段でありますので、ORMで品質が良くなるのであれば、ガンガン導入していきたいです。

については「品質が良くなるのであれば」使う事に抵抗感はないのですが。
「縛りの結果、変な箇所が生まれる」のであれば、その部分の品質が低下してしまうので、そういった部分にまで「むりくりにORM縛りをする」のは、あんまり好まないかなぁ、と。

んで「むしろ、SQLを書いちゃうと、オブジェクト化ができなかったりします。」でデメリットが出るのであれば、選択肢としてはどちらかというと「んじゃ、ORM使わないほうが効率よくない?」って判断になり得るんじゃないかなぁ? とか思う訳です。


あと、もう1つ興味深い、こちらはスライドを。
https://www.slideshare.net/kwatch/how-topreventorm-troubles

しかし ORM は、アプリケーション開発者にとっては便利でも

わりとそうでもない(苦笑

ORMによるトラブルは (たいてい) 解決策がある

「たいてい」ってあたりに、色々と味わいを感じてしまいます(苦笑

なんてちょっと軽いところに茶々を入れながら。

背景:ORMによるトラブルが多発
ORMが生成するSQLがクソ いわゆる「ぐるぐる系SQL
SQLをろくに勉強しないアプリ開発者 そのくせOOPデザインパターンを得意げに語る ;(
開発効率を上げるためのORMなのに話が違う! ある面では効率が上がっても、別の問題を引き起こしている

この辺、色々と闇の深さを感じます………

DBAがORMを知らなすぎる

この辺は「ど~なんだろうなぁ?」的な。
おいちゃんが「(DBAではなく)プログラマ」だから、「ORMはプログラマが把握してDBAに説明すべきもの」なんじゃないかなぁ? って思ってしまうので、「DBAがORMを知らないのはヨクナイ!!」とは、あんまり、考えたことがないかも。

んでまぁ……理想が「アプリ開発者がSQLを勉強してくれる」になっているあたり、が、なんとも(苦笑
いや大事だと思うんですよSQL理解するのは。
ただ、この発言の背景にある「アプリ開発者のSQLの不勉強っぷり」に思いを馳せると、色々と、ねぇ………

Prepared Statementではwhere句への追加 などが行えない

については、ここで書いてあるテンプレートやクエリオブジェクトを使う以外にも、割と色々と手段があるので、ぶっちゃけ「困ったことはない」ですねぇ。

ORMでよくあるトラブル

「クエリ発行箇所が特定できない」は、言われてみれば確かに、ですねぇ。
index付け忘れとかは、論外付近の事象ではなかろうか、と。

開発者からのSQLの相談に乗ってあげる

相談に乗って貰えるんなら、是が非でも、是非とも……

心構えその5:金を積む

大爆笑www
でもまぁ「真理でございます」w

あと。

個人的にお勧めなO/Rマッパーは? ?

のところで

自作!自作マジお勧め! 職人が自分の道具作って何が悪い!車輪の再発明なぞ知らんがな!

とかあるのは、心から爆笑するとともに、深くうなずける一文でございます。

ActiveRecord?あれはあんまり…

そなの?
この辺はよくしらない。

あと、地味に「PHPでお勧めのORMが出てない」のが、なんとも………。


とまぁ、ざっくりまとめると。
おいちゃんの見解としては
・軽いものはORMで書くけどやっかいなやつはSQLダイレクトに書いたほうが早い事が多いよねぇ
ってのをベースに
・チーム内に「ORMに長けたメンバーがいて厄介な質問でも回答が帰ってくる」んならORM側に寄せますがそうでなければSQL側に寄せます
って感じかなぁ、と。

もうちょっと、ORMの裏側の仕組み&コードの書き方がこなれてきたら、もうちょっと印象が変わるような気がするんですけどねぇ。
現状だと「全部ORMのみで、SQLは一切書かない」は、案件にもよるんだけど、ちょっと選択肢としては「難しい事が多い」かなぁ、と。

ただまぁ、つまり「生SQLも比較的容易に飲み込める」タイプのORMであれば、「シンプルなSQLはORMのほうが手っ取り早い」のも事実なので、割と積極的に使いたいところではあるんですよねぇ。
なので、別に「ORM死すべし」とは思ってないあたりが、とてもどっちつかずだなぁ、と(笑

*1:マテ

*2:つまり「一定量存在する」

*3: (C)千鳥 ノブ

プログラムを学ぶための本といえば

どっちも、まだ「プログラムを始めた頃」だと少し難しかったり不思議だったりする内容なのかもしれないんだけど。
とはいえ、一端は頭の中に「なんとなく」でもいいから、インストールしておいたほうがよいんじゃないかなぁ、と思う書籍を2つ。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード、は、結局のところ「保守メンテがしやすいコードの書き方」なので、早めのタイミングでお作法として理解していたほうが、色々と(内外に)トラブルを抑止できます。
コーディングを支える技術、は、言語を「少し俯瞰して」見る事ができるので、プログラマを己の飯の種の1つにするのであれば、読んでいて損のない1冊です。

リーダブルコードは、近い書籍がほかにも数冊出ているので、そういった書籍も一緒に読んでみてもよいんじゃないかなぁ、と思います。

Slimは本当にスリムなのだろうか?

ちょいと気になったり興味があったりしたので、いくつかの「比較的(日本で)ポピュラーと思われる」PHP Web Application Framerowkの、行数とかファイル数とかを調べてみました。
まずインストールですが、本日のタイミングで

composer.phar create-project laravel/laravel laravel
composer.phar create-project cakephp/app cakephp
composer.phar create-project symfony/framework-standard-edition symfony
composer.phar create-project kenjis/codeigniter-composer-installer codeigniter
composer.phar create-project slim/slim-skeleton slim

という感じで一通り入れています。
バージョンとかの細かい違いがあると思うので、その辺は適宜脳内補完をお願いいたします。

また、ファイル数や行数は、基本的には以下のように調べています。

#
find vendor/ -name "*.php" | xargs  wc -l | tail -n 1
find vendor/ -name "*.php" -type f | wc -l
#
find FW本体と思われるディレクトリ -name "*.php" | xargs wc -l | tail -n 1
find FW本体と思われるディレクトリ -name "*.php" -type f | wc -l

ただ、上述だと「ファイル数が多くてうまく合計されない」ものもあるので、その辺は適宜よしなにしております*1

結果。

フレームワーク 全体ファイル数 全体行数 FW本体と思われるディレクト FWファイル数 FW行数
laravel 6161 832270 vendor/laravel/framework/ 857 127125
symfony 6134 739122 vendor/symfony/symfony/ 4055 466309
cakephp 4469 598447 vendor/cakephp/ 872 135415
codeigniter 258 78948 vendor/codeigniter/framework/ 199 68492
slim 1424 145223 vendor/slim/slim/ 47 8564

Laravelが重量級なのは想像通り、cakephpもそれなりに。
symfonyが、思いのほか「フレームワーク本体(と思われる……もしかしたら本体以外が混ざってるのかも)」のファイル数&行数が膨大で、ちょいとびっくり。
codeigniterは「全体としては軽い」んだけど「本体はそこそこの重量」があって。
slimは、「周辺まで含む」と少し行数ファイル数ともあるものの、本体はさすがに貫禄の軽量っぷりw

こうやって見ると、改めて「フレームワーク"を"読む」なら、Slimから、ってのは、割と入門的な位置に行けそうだなぁ、と、改めて感じるところでございます。

*1:findをリダイレクトして「行頭に wc -l」の置換かましたのを実行した結果をリダイレクトたものの0x20をタブに置換してExcelでsum w

この本は外せない

何はなくとも、レベルで必読な書籍

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

書いてある内容は割と平坦で「技術素養が薄くても普通に読める」んだけど、そこにあるエッセンスは「業務を二桁年重ねてきたエンジニアでさえも割と失念しがち」な、本当に大事なことが色々と書かれていると思う。

SlimとSlim-Skeleton のindex.php

公式サイト http://www.slimframework.com/ のサンプルだと、index.php

<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;

require 'vendor/autoload.php';

$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
    $name = $args['name'];
    $response->getBody()->write("Hello, $name");

    return $response;
});
$app->run();

と書かれている一方で、「実際にプロジェクトを起こす時にベースに使うと便利」とされている、公式から出ているSlim-Skeletonのindex.php では、少しばかり、差異があります。
この辺を少し、読み解いていきましょう。

注意
一時期以降、結構ごっそりと書き方が変わっています。
一端 https://github.com/slimphp/Slim-Skeleton/tree/439b6ec6cb1189d0be7fe31f87af4cbeeb801e53 を前提に記述、後で最新用のフォローアップをします。


さて。
とりあえずまずはルーティングの設定です。

$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
    $name = $args['name'];
    $response->getBody()->write("Hello, $name");

    return $response;
});

このあたり、ですね。
これを、Pageが増える毎にindex.phpに重ねていくと、index.phpがえらいこと長くなってしまうので、ちょいと切り出してみましょう。
そうですねぇpublicにあるのも些か好ましさに欠けるかと思いますので。
publicディレクトリと同じレベルに、srcというディレクトリを切って、その中に routes.php とかいうファイルを作ってみましょう。

そうすると、まず切り出すので、index.php

<?php
require 'vendor/autoload.php';

$app = new \Slim\App;

// Register routes
require __DIR__ . '/../src/routes.php';

$app->run();

こうなり、切り出したroutes.php

<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
// Routes
$app->get('/[{name}]', function (Request $request, Response $response, array $args) {
    // Sample log message
    $this->logger->info("Slim-Skeleton '/' route");
    // Render index view
    return $this->renderer->render($response, 'index.phtml', $args);
});

こうなります。
use句は、切り出したほうのコードで使ってるので、併せての移動ですね。
ついでに、 Psr\Http\Message\ServerRequestInterface はインタフェース宣言だけなので&Slimは実際にはちゃんとクラスまで切っているので。
implementsしたクラスに、use句を変更しておきましょう。

<?php
use Slim\Http\Request;
use Slim\Http\Response;
// Routes
$app->get('/[{name}]', function (Request $request, Response $response, array $args) {
    // Sample log message
    $this->logger->info("Slim-Skeleton '/' route");
    // Render index view
    return $this->renderer->render($response, 'index.phtml', $args);
});


さて。
index.phpですが。
このままでもよいのですが、実際にアプリケーションを組むのであれば「なにがしかの設定値」は、やはり欲しいところになります。
new \Slim\App でも、コンストラクタに色々と実際には設定を渡す事ができるので。

と言うわけで、設定値をやはり切り出して記載してみましょう。
ファイルは、src/settings.php にしてみます。

index.php

<?php
require 'vendor/autoload.php';

// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);

// Register routes
require __DIR__ . '/../src/routes.php';

$app->run();


settings.php

<?php
return [
    'settings' => [
        'displayErrorDetails' => true, // set to false in production
        'addContentLengthHeader' => false, // Allow the web server to send the content-length header
        // Renderer settings
        'renderer' => [
            'template_path' => __DIR__ . '/../templates/',
        ],
        // Monolog settings
        'logger' => [
            'name' => 'slim-app',
            'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
            'level' => \Monolog\Logger::DEBUG,
        ],
    ],
];

これで「settings.phpに設定を書く」と「new \Slim\Appの引数として渡される」ので、色々な設定を入れ込む事ができるようになります。

次に。
diコンテナも少し設定をしておきましょう。
とりあえずざっくりと「view用のクラスとしてPhpRenderer」と、最低限の「logger(ログを出力するクラス)」くらいは追加しておきましょう。
追加用の設定は src/dependencies.php にでもいれておくことにします。

index.php

<?php
require 'vendor/autoload.php';

// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);
// Set up dependencies
require __DIR__ . '/../src/dependencies.php';

// Register routes
require __DIR__ . '/../src/routes.php';

$app->run();


dependencies.php

<?php
// DIC configuration
$container = $app->getContainer();
// view renderer
$container['renderer'] = function ($c) {
    $settings = $c->get('settings')['renderer'];
    return new Slim\Views\PhpRenderer($settings['template_path']);
};
// monolog
$container['logger'] = function ($c) {
    $settings = $c->get('settings')['logger'];
    $logger = new Monolog\Logger($settings['name']);
    $logger->pushProcessor(new Monolog\Processor\UidProcessor());
    $logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
    return $logger;
};

これで一段落……したいところなのですが。
ついでに、middleware用の設定をいれる場所を作っておきましょう。
一端「場所だけ」を作る感じで、 src/middleware.php に用意をしておきます。

index.php

<?php
require 'vendor/autoload.php';

// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);
// Set up dependencies
require __DIR__ . '/../src/dependencies.php';
// Register middleware
require __DIR__ . '/../src/middleware.php';

// Register routes
require __DIR__ . '/../src/routes.php';

$app->run();


middleware.php

<?php
// Application middleware
// e.g: $app->add(new \Slim\Csrf\Guard);

こんなところでしょうか。
ちなみに、サンプルで書かれている「\Slim\Csrf\Guard」は、これそのものではなくてもよいので、なにがしかCSRF対策用のものをいれておくのは、大変に好ましいと思われます。

これで、おおよそ「Slim-Skeletonのindex.php」と同じもの、になります。
実際のSlim-Skeletonのindex.php

<?php
if (PHP_SAPI == 'cli-server') {
    // To help the built-in PHP dev server, check if the request was actually for
    // something which should probably be served as a static file
    $url  = parse_url($_SERVER['REQUEST_URI']);
    $file = __DIR__ . $url['path'];
    if (is_file($file)) {
        return false;
    }
}
require __DIR__ . '/../vendor/autoload.php';
session_start();
// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);
// Set up dependencies
require __DIR__ . '/../src/dependencies.php';
// Register middleware
require __DIR__ . '/../src/middleware.php';
// Register routes
require __DIR__ . '/../src/routes.php';
// Run app
$app->run();

となっていて。
空改行の箇所をのぞくと
・先頭の9行
・session_start();
くらいの差異、ですね。

先頭の9行は大雑把に
・ビルトインウェブサーバーを使っている時に「staticなURI(実在するファイルのURI)がcallされたら、プログラム的な処理をしない」ためのロジック
になります。

session_start()は、そのまま。
まぁセッションを使うことは「極めて多い」ですからねぇ。


と、このような感じで。plainのSlimのindex.phpから、Slim-Skeletonのindex.phpに移り変わりが行われています。

……という感じだったのですが。6 Nov 2018の
https://github.com/slimphp/Slim-Skeleton/tree/8dd2f8469514a43d4180148466a832f8d1683fe4
のタイミングで、ちょいと諸々、がらっと様変わりをしたようなので、少し覗き直してみましょう。

index.phpですが、こんな風になっています。

<?php
if (PHP_SAPI == 'cli-server') {
    // To help the built-in PHP dev server, check if the request was actually for
    // something which should probably be served as a static file
    $url  = parse_url($_SERVER['REQUEST_URI']);
    $file = __DIR__ . $url['path'];
    if (is_file($file)) {
        return false;
    }
}
require __DIR__ . '/../vendor/autoload.php';
session_start();
// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);
// Set up dependencies
App\Dependencies::init($app);
// Register middleware
App\Middleware::init($app);
// Register routes
App\Routes::init($app);
// Run app
$app->run();

全体的に「クラス名::init($app)」の方法でのcallに変わっている感じですね。
例えばRoutesを見ると

<?php
namespace App;
use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
/**
 * Configures the routes
 * @param App $app
 */
class Routes
{
    /**
     * Configures the routes
     * @param App $app
     */
    public static function init(App $app)
    {
        $container = $app->getContainer();
        $app->get('/[{name}]', function (Request $request, Response $response, array $args) use ($container) {
            // Sample log message
            $container->get('logger')->info("Slim-Skeleton '/' route");
            // Render index view
            return $container->get('renderer')->render($response, 'index.phtml', $args);
        });
    }
}

という感じで、全体的に「クラスの中のstaticなメソッドでの定義」に変わっているようです。

で、その状況が、わずか二週間弱、19 Nov 2018 に
https://github.com/slimphp/Slim-Skeleton/tree/15fc5968d9430e67110d82dff3b1b3a2349c9cf6
で、もう一度変わっています。

<?php
if (PHP_SAPI == 'cli-server') {
    // To help the built-in PHP dev server, check if the request was actually for
    // something which should probably be served as a static file
    $url  = parse_url($_SERVER['REQUEST_URI']);
    $file = __DIR__ . $url['path'];
    if (is_file($file)) {
        return false;
    }
}
require __DIR__ . '/../vendor/autoload.php';
session_start();
// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);
// Set up dependencies
$dependencies = require __DIR__ . '/../src/dependencies.php';
$dependencies($app);
// Register middleware
$middleware = require __DIR__ . '/../src/middleware.php';
$middleware($app);
// Register routes
$routes = require __DIR__ . '/../src/routes.php';
$routes($app);
// Run app
$app->run();

で、routesが

<?php
use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
return function (App $app) {
    $container = $app->getContainer();
    $app->get('/[{name}]', function (Request $request, Response $response, array $args) use ($container) {
        // Sample log message
        $container->get('logger')->info("Slim-Skeleton '/' route");
        // Render index view
        return $container->get('renderer')->render($response, 'index.phtml', $args);
    });
};

前回「staticなクラスメソッド」だったのが、今回は「無名関数」に置き換わっている感じですね。

なんか色々と動きがありそうな感じなので、ちょっと興味深く思いました。

明日になったら「明日」になるから、多分、綺麗にならない

なんかつい最近も似たような事を書いたような記憶が、なくもないのですが(
ソレはきっと、ビューティフル・ドリーマー - gallu’s blog )。

今回は(も)、端的には「技術負債を作らないために」的な話でございます。
んと……定期的に耳にするのが
・今は時間がないから、仕方がないから汚くてもまず実装して仕上げる
的なお話。

まぁ「汚い」のレベルにもよるのですが、一旦「そのままにすると負債になるレベル」と仮定します。
「時間が無いから締め切りがあるからやむを得ず一端」というのはわかりますしきっとその気持ちに嘘はないのだろうと思うのですが*1、端的には
・いつまでも負債は支払われず、それは重くのしかかってそのうち自重崩壊する可能性が上がっていく
事が多いかなぁ、と思われます……ってのを、もう少し噛み砕いて。

どこかで何か所かで拝見している、意味合いとしてはこんな内容の文章がありまして。
「綺麗に書ける人は時間をかけなくても大抵綺麗なコードを書くし、汚く書く人は時間を与えても大抵汚いコードを書く」
これを見た時に「あぁ、たしかにある程度そんなもんだろうなぁ」とか思ったりすることが、少なからず。

或いは「一旦忙しいから(ダメかもしれないけど)今までの慣習通りで書いて、しっかりした考察は後回し」ってケースが、時々、見受けられます。
まぁ「締め切り直前」とか、気持ちが「全く完全に1mmも理解できない」とは言いませんが。が。が。

ちぃと違う切り口としましては、曰く
「ベテランは"後で直せる"事を理解しているから、簡単に書く。非ベテランは"後で直せる"事を理解していないから、後で直さなくてすむように複雑に時間をかける(けど、後で直さなくてすむなんてことはあり得ない)」
けだし名言だなぁ、と思うところでございます。

汚く書く人の一番困るのは「後で直しにくいような設計で書く」のが一番の困り者でして……プログラムにしてもデータ設計にしてもテーブル設計にしても。
「綺麗にクラスが切れていて綺麗にメソッドが切れているんだけどメソッドの実装がちぃと泥臭い」であれば「そのメソッドを書き直す」とかで片付くことが多く、ちゃんとテストがかかれていれば「書き直した、テストした、OK」で終わるので、そんなに高い負担にはならないものでございます。
データ設計やテーブル設計だと「後での追加が比較的容易になるように丁寧に設計して、その詳細項目は一旦、欠けのある雑な設計」だと、「足りなくなったら追加しよう」で事足りるので、やはり、そんなに高い負担にはならないものでございます(まぁ「プログラム側が、カラムの追加に対してDRYであること」ってのが追加で入ってきますが)。

つまり、「汚い実装」は、割と多くの場合において「そもそも設計レイヤーでコケている」ケースが多い、というのがまず1点目としてあります。

で、その1点に追加をすると。
「綺麗な実装」ってのは、知っている限りでは「それなりに脳みそで汗をかいてもんどりうった先にある」事が多いように思われるのでございます。
つまり、ある程度の「熟考して立ち止まって3歩すすんで2歩さがってスクラップアンドビルドを繰り返して無理な要求とリファクタで悲鳴を上げながら」スキルが上がってくる側面が、どうも、あるように思われます。
その辺をやらずに「とりあえず汚いコード/テーブル設計を書き散らかす」を繰り返すと。「綺麗な実装」に対する経験値が積めないまま時間が過ぎ去ってしまう、ってのが、どうもちらほらとあちこちで、ごくまれに、例外的に*2見かけられるように思われるのですが、如何でしょうか?

次に「急いでるから一旦汚く書く」ケースで「じゃぁ、いつ、リファクタするの? そのスケジュールは具体的に抑えてるの?」って質問がありまして。
……書いたの最近だなぁ ソレはきっと、ビューティフル・ドリーマー( https://gallu.hatenadiary.jp/entry/2019/02/05/012839 )にある通りなんですが「そのうち」ってのは「いつまでもやってこない」時間軸なんですよねぇ大概。
だって未来は暇じゃなくて「次の作業」が鮨詰めに詰められているんですもの。

で、ってことは「あとで」は来ないので「綺麗に書く経験も苦労も悲鳴も怒号もないまま」に進んでしまうので。
そうすると結果的に「いつまでもコードが綺麗にならない」し「綺麗に設計するための修練を積むことができない」から結果として「汚いままで綺麗にする術がない」んですよね。

勿論「運用で苦労するもの」ではあるんですが、……その辺、認識できないと「他人に運用を押しつけて回避」とか「運用なんてこんなもんだ」くらいで、意識が出来ないまま無駄なコストを食らいまくっていくものでございます。
「極寒の地で全裸で凍えながらなぜ つらいのかわかっていないようなもの これ以上 心身に負担をかけると死にかねないよ」とは、けだし名言だなぁ、と。
「寒いのは当然、凍えるのは当然」とか言っていると、そのうち凍傷になって、場合によっては絶命するんですよねぇ。サービスにしてもエンジニアにしても会社にしても。

勿論案件なので「締め切りがある」ってのは重々承知しているのですが。
とはいえ、どこかで巻き返さないと、なんとなし「ジリ貧にならん?」とか思ったりするんですよ。
「巻き返してそれなりに修練を積むことの重要性」って、なんとなく、割と軽視されているように見えるんですが。

その辺、ど~なんですかねぇ??

*1:「お気持ち」だけなら、ねぇ……

*2:≒大量に、頻繁に

新学期も始まったのでまずは「学び方」

ちょいとラインナップも変更になってきたので、頑張ってこの1年、また備忘録していきたいと思います。

まず初っぱなは「学び方」を学びましょう、ということで、毎度おなじみこちらから。

アプレンティスシップ・パターン ―徒弟制度に学ぶ熟練技術者の技と心得 (THEORY/IN/PRACTICE)

アプレンティスシップ・パターン ―徒弟制度に学ぶ熟練技術者の技と心得 (THEORY/IN/PRACTICE)

併せて、これも毎年なんですが。

Bjarne Stroustrup: 標準C++を新しい言語として学ぶ
こちらから、この文章も、毎度毎度、使わせていただいております。

プログラムは書きやすく、正確で、保守可能であり、受け入れられる程度に 効率的であることが望ましい。したがって、この理想に最大限に近づけるように C++(そして、その他のプログラミング言語)を使用すべきである。

それではこの1年、頑張って行きましょう!!