こんな状況でした
- システムの全体像を把握しきれない
- 前任者からの引き継ぎ
- メンテされた仕様書等ドキュメントなし
- 搭載されている機能が多すぎる
- テストコードは(当たり前のごとく)存在しない
- システムで使われているアーキテクチャが古い
- Java 5 / tomcat 6.0.9(サポート終了)
- Adobe flex 2(サポート終了)
- PHP5.4(公式サポート終了)
経緯と動機
上記のような「レガシー」な社内Webシステムに対して機能追加をする案件を担当することになりました。システムを少しでも「イマドキ」なシステムに近づけたい。しかし、テストコードやドキュメントが無い中で大幅なテコ入れは厳しい。システム全体のリプレースも検討するも、コスト的な問題でNG。。
おとなしく現実に向き合い、開発に取り掛かり始めたのですが、開発を進めていくうちに以下のような課題にも気づきました。
こんな状況でもありました
- ソースコードの管理が不十分・イマドキでない
- GitではなくSubversionを用いている
- (PHPプログラムに関して)本番環境のコードとSubversionリポジトリ内コードが一致しない(最新のコードがコミットされていない)
- ビルドツールが導入されていない(Java)
- Eclipse上でボタンをポチポチ
- もちろん口伝で引き継ぎ
- Eclipseバージョン指定&Eclipse環境設定のコピーとか
- Eclipse上でボタンをポチポチ
- 連携システムのIPアドレス設定を設定ファイルで記述している(PHP)
- 環境ごとに手動でコードを書き換える必要がある
- ローカル開発環境が整備されていない
- 検証用マシンを1台用意して、複数人で共有
- 誰かが使っているときは検証できない
そこで、アプリケーションのコード自体のリファクタリングではなく、上記のような開発環境まわりの改善を図ることで(厳密にはコード改修に関する項目もありますが..)開発効率を少しでも向上させよう、という目的のもと、チーム(3人)でいくつか取り組みを行いました。
記事の目的
同じようなレガシーシステムを担当されている方にとって、少しでも改善のヒントとなれば、というのと、新規システムや比較的新しいシステムであればぜひとも取り入れるべき取り組みとなってくるのかな、という2つの意味で投稿させていただきます。
注意点
- チームでの開発を想定した改善活動になります
- 本記事の取り組みが全てのプロジェクトに適用できるとは限りません
- リグレッションテストが十分にできない時点で、これらの取り組みが一切許容されないプロジェクトもあるかと思います
- この記事では紹介している1つ1つの取り組みについてはあまり深くは触れません
- 技術・キーワードとしては聞いたことはあるけれど、実際の現場でどこまで導入できるのかの1つの参考事例として読んでいただければと思います
- 参考記事のリンクを後で貼っておきます..
- 技術・キーワードとしては聞いたことはあるけれど、実際の現場でどこまで導入できるのかの1つの参考事例として読んでいただければと思います
- Adobe flexについては今回の改善対象から外しております
- 色々調べたのですがどうにも改善の糸口が見つからず...
取り組み
1. Gitでのバージョン管理
既にチームでGitLabを利用していたので、Gitに移行しない手はありませんでした。git-svnを用いてSubversionからGitへ移行を行いました。 git svn clone
で即完了。と思っていたのですが、subversionのリポジトリサーバがsshで標準ポート以外を使っていた影響で大苦戦しました。
最終的にはsubversionのリポジトリ( conf
, db
, hooks
等があるディレクトリ)をコピーして、そのディレクトリに対して git svn
コマンドを実行しました。
$ git svn clone -s --prefix=svn/ file://[レポジトリを保存したディレクトリ]
また、PHPのプログラムについては本番環境のコードを運用チームに依頼して入手し、最新のソースとしてコミットを行いました。
2. ビルドツールの導入(Java)
Eclipse以外のIDEを使えないというの点が、IntelliJを最近導入した当チームにとっては問題となりました。また、GUI操作の手順を担当内で共有する・後世に引き継ぐのは厳しいものがあります。。
チームでGradleを利用した実績があったので、本プロジェクトでもGradleの導入を検討しました。Gradleを導入することで、前述のGUI操作に該当する部分を設定ファイル(build.gradle
)で管理することが可能になり、後述のコマンド1発叩くだけで誰が実行しても同一の成果物が生成できるからです。
しかし、プログラム全体の挙動に関わるところであり、リグレッションテストが十分に行えない状況である点も考慮し、ローカル開発環境での利用に限定することと、最終的な試験やデプロイの際には従来どおりのビルドで生成されたモジュールを利用するという条件のものと、ビルドツールの導入を行いました。
なお、Gradleの実行にはJDK8を利用しております。
依存ライブラリの調査
Gradleを導入するにあたり、既存の依存ライブラリを調査してbuild.gradle
のdependencies
に記載する必要がありました。
ソースコードにおける依存ライブラリディレクトリの各jarファイル内のMANIFEST.MF
のImplementation-Version
やファイル名を参考に、mavenリポジトリ検索サイト(こことか)を利用してbuild.gradle
に記載すべき内容を調査し、記載を行いました。
(外部から取得できないものについては、一部compile files()
を用いてローカルのjarファイルを指定しました)
build.gradle
こんな感じになりました
plugins {
id "war"
}
sourceCompatibility = '1.5'
targetCompatibility = '1.5'
sourceSets { // 特殊なディレクトリ構成だったため設定が必要でした
main {
java { srcDir 'WEB-INF/src/main/java' }
resources { srcDir 'WEB-INF/src/main/resources' }
}
test {
java { srcDir 'WEB-INF/src/test/java' }
resources { srcDir 'WEB-INF/src/test/resources' }
}
}
repositories { // mavenCentralから取得できないライブラリの取得元を追加しています
maven { url "http://maven.imagej.net/content/repositories/public/" }
mavenCentral()
maven { url "http://repo.metova.com/nexus/content/groups/public/" }
maven { url "https://www.seasar.org/maven/maven2/" }
}
dependencies {
compile 'javax.activation:activation:1.0.2'
compile 'aopalliance:aopalliance:1.0'
compile 'commons-codec:commons-codec:1.3'
...
compile files('WEB-INF/lib/h2-2007-04-29.jar')
...
compile 'org.seasar.dao:s2-dao:1.0.46'
compile 'org.seasar.dao:s2-dao-tiger:1.0.46'
...
}
ビルド
以下のコマンドを実行することで、デプロイに必要なwarファイルが作成されます。
$ ./gradlew build
3. 環境変数による連携システムIPアドレス切り替え(PHP)
ローカル開発環境・ステージング環境・本番環境、それぞれで連携システムのIPアドレスが異なるのですが、従来はそれぞれの環境にデプロイした後に手動で1つ1つ切り替え作業を行っていました。
連携システムが1個や2個であればさほど問題にならないかもしれませんが、数が増えてくると作業が面倒になり、また万が一設定を誤った際の事故(誤って本番環境につないでしまう等)のリスクも高まります。
そこで、以下のようにPHPのプログラム内で環境変数を取得し、それに応じてプログラム内で使用する変数の値を切り替えるような挙動をするように修正しました。
define('APP_ENV', getenv('APP_ENV'));
if (APP_ENV === 'local') {
$urlA = "http://localhost/appA.php";
$urlB = "http://localhost/appB.php";
...
} elseif (APP_ENV === 'staging') {
$urlA = "http://staging.example.com/appA.php";
$urlB = "http://staging.example.com/appB.php";
...
} elseif (APP_ENV === 'production') {
...
また、ステージング環境・本番環境ではapacheの設定ファイルに以下を追記し、環境変数が設定されるようにしました。
<VirtualHost *:80>
...
SetEnv APP_ENV staging # 追記部分 本番の場合はproduction
...
</VirtualHost>
4. Dockerを用いたローカル開発環境の整備
各種実行環境のセットアップに加え、PHPのデバッガ(xdebug)導入も検討していたので、一人ひとり環境を構築するのはとても面倒そうだなと思い、Dockerを使ってローカル開発環境を整備することにしました。
Java-Tomcatのローカル開発環境
FROM centos:latest
ADD jdk-1_5_0_22-linux-amd64.bin /root/
RUN set -x && \
echo yes | /root/jdk-1_5_0_22-linux-amd64.bin
ENV JAVA_HOME=/jdk1.5.0_22 \
PATH=$PATH:/jdk1.5.0_22/bin
ADD apache-tomcat-6.0.9.tar.gz /root/
ENV CATALINA_HOME=/root/apache-tomcat-6.0.9
EXPOSE 8080
CMD ["/root/apache-tomcat-6.0.9/bin/catalina.sh", "run"]
java-docker:
image: ...
ports:
- "8080:8080"
volumes:
# 2.でビルドしたwarファイルを同ディレクトリに配置するとデプロイされる
- "${PWD}/app.war:/root/apache-tomcat-6.0.9/webapps/app.war"
environment:
- TZ=Asia/Tokyo # タイムゾーンの指定 ログ出力とかで設定しておくと便利
restart: always
PHPのローカル開発環境
FROM centos:7.4.1708
RUN set -x && \
yum update -y && \
yum install apache php php-devel php-pear gcc make -y
RUN set -x && \
pecl install xdebug-2.4.1
ADD xdebug.ini /etc/php.d/
RUN ln -sfT /dev/stdout "/var/log/httpd/access_log" && \
ln -sfT /dev/stderr "/var/log/httpd/error_log"
EXPOSE 80
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
php-docker:
image: ...
ports:
- "80:80"
volumes:
- "${PWD}/../srcDir:/var/www/html"
environment:
- APP_ENV=local # 3.の環境変数
- TZ=Asia/Tokyo # タイムゾーンの指定
restart: always
また、これらをbuildして生成されたコンテナイメージをプライベートリポジトリ(Docker Registry)にpushをしました。
その結果
$ docker pull ...
のコマンド一発で、チーム全員のローカル開発・デバッグ環境を構築することができるようになりました。(その後のIDE連携は手動で対応が必要でしたが...)
まとめ
今後どれだけ長く使い続けられるかわからないシステムに対して、どこまでコストをかけるかは難しい問題です。(特に2.については手間がかかった割にはあまりその恩恵が受けられていないような気もしています。。)
今回の取り組みは、自分の技術的な勉強も兼ねて積極的に取り組んだ節もあるため、冒頭にも述べたとおりどこまで何をやるかの判断はプロジェクトに応じて変わってくるかと思います。
最新の技術を使ったイマドキなサービスを、高性能なマシンを使って開発したい。しかしながら現実はそう甘くはないようですね。。