散財したくなったのでAppleTV買いました。
AirPlay目当てで買ったんですが、AirPlayはMacBook Air(Late 2011)以降の対応だそうです。(持ってるのはMacBook Air Mid 2010)
やっちまった・・・。
散財したくなったのでAppleTV買いました。
AirPlay目当てで買ったんですが、AirPlayはMacBook Air(Late 2011)以降の対応だそうです。(持ってるのはMacBook Air Mid 2010)
やっちまった・・・。
出す出す言ってなかなか出来なかった怖話iPhoneアプリが出来ました。
http://itunes.apple.com/jp/app/bu-hua/id564486792?ls=1&mt=8
App Store初申請なので最初は絶対リジェクトされると思ってたんですがあっさり通ってびっくり。
@jishihaさんに聞いた「iOS6対応で申請がいっぱいきてるからそのどさくさで簡単に通るとおもいますよ」というここを強い言葉を信じでえいやと提出してよかったです。
結局、PhoneCap -> Titanium -> PhoneGap -> Nativeというように紆余曲折を経ました。
星5のステマしておきました。
お陰様でsvnからGithubに移行。気軽にブランチ作れてとっても快適。lingrやjenkinsとの連携もやっぱりスムーズ。@hrysdとはしゃぎました。
i18n関連が面倒だと書いたらコメント欄でk1LoWさんが教えて下さったcakeplusプラグインを入れてみた。超改善された。あざーす!
以前も書いたが、全コントローラー、全アクションに認証処理がベタ書きされてる。そこでその処理をLegacyAuthComponetという名前で分けた。
<?php /** * 旧認証 * * 古い認証機能をラップしたもの。 * 管理者ログインしていない場合はログイン画面に * リダイレクトする * * 使い方: * class FooController extends AppController * { * public $compornents = array('LegacyAuth'); * } */ class LegacyAuthComponent { public $name = 'LegacyAuth'; public $controller; public function startup(&$controller) { $this->controller =& $controller; $this->adminLoginRequired(); } public function adminLoginRequired() { if (isset($_SESSION['ses_login']['id'])) { $id = $_SESSION['ses_login']['id']; } else { $id = null; } if (!($id > 0)) { $this->controller->Session->setFlash(__('admin_login_required', true)); $this->controller->redirect('/login/'); } } } ?>
一人だけでいいんだけどとにかくPHPプログラマーが見つからない。PHPの老舗、アシアルさんに相談したが、直近では空いている人がいないとのこと。しかし同じくPHP老舗であり、PHPCon2012でもニアミスしていたアリウープの柏岡様を紹介していただいた。
なんとこのブログの記事を読んでいたとのことで、ぶっちゃけたところからお話することができてとても話が速かった。しかしアリウープさんもお忙しいとのことでまだ決まるかどうかわからない状態。
それとは別で以前、ちょっと一緒にやっていくのは難しそうと書いた会社様より、「具体的に何が悪いのか、どういう人材を探しているのか説明して欲しい」との電話があり、上司の方とお話した。
どうも、こちらの話しているユニットテスト/自動テストと先方の話しているテストが違っているような気がする。
「よくわからないけど要はそういうツールがあるんですよね?だったらうちもできますよ。普通やりますよね、自動テスト」という感じ。
「でも先日見せてもらったコードにはユニットテスト無かったですよね?」(ついでにいうと使ってるライブラリはPEAR DBとSmartyだけでしたよね?)というようにちょっと話が噛み合わなかった。
僕らはそんなにすごいプログラマーを募集してるわけじゃないんです。ただ、プライベート(仕事外)でコードを書いてるPHPプログラマーだったら誰でも歓迎なんです。
しかしSIerから離れて随分経つので忘れていました。世の中にはプライベートでコードを書かないプログラマーが沢山いるという事実を・・・。
この日記が終わるかもだと・・・?
ハハハ、あんなメール、俺にとってはご褒美だ。
appディレクトリ以下から対象を抽出し、potファイルを生成する。
% cake i18n extract
potファイルをpoファイルにマージする。
% msgmerge -U app/locale/jpn/LC_MESSAGES/default.po app/locale/default.pot
poファイルやmsgmerge等のツールについてはgettextをググる。
様
お世話になっております。フィヨルドの駒形です。
下記大変申し訳ありませんでした。NDAも理解しております。 該当文章をブログから削除しました。
私がどのような意図で様に関するブログを書いたのか 簡単に説明しておきたいと思います。 (そもそもブログを書くのはとても工数のかかる作業なので 理由無く書きません)
私はAAAAやBBBBに限らず、殆どのシステムのアイデアや実装は 簡単に真似できるので殆ど価値が無いと思っています。 (2日で概要が出来るぐらいなので)
ASP等のシステムの場合、そこに集まるユーザーやデータ、そしてその システムを維持し、改善していける組織は今体験しているように 簡単に真似することができません。 (なので弊社のシステムもオープンソースにしています)
「FJORDのWebサービスはオープンソースです ≪ FJORD, LLC」
http://fjord.jp/love/534.html
最初の方のブログにも書きましたが、「ヒドイ状態」などと笑い話になりますが、 業界的には非常にありふれた状態で、誰しも大っぴらに言えないだけで 周りにそういうシステムが沢山あります。
表向きにはいい言葉だけを並べますが、実情は大抵のシステムがヒドイ 状態です。そこを隠さず、真正面から改善し、内情すら公開して自信を持って 提供できるようになった時、ものすごい信頼を得られるサービスになり、 売上が大幅に上がるのではないか、そしてそういう態度がそれを是とする 開発チームの構築につながると考えたためです。
とはいえ、そんなことは私が勝手に考えて勝手にやったことなので ご迷惑をお掛けしてしまって申し訳ございませんでした。
他にも問題ある点があればご指摘いただければと思います。
以上、宜しくお願い申しあげます。
--
----------------------------------------------------------------------------------
Masaki Komagata
ブログ: http://docs.komagata.org/
FJORD, LLC: http://fjord.jp/
CMS for Cloud: http://lokka.org/
スマホで怖い話: http://kowabana.jp/
----------------------------------------------------------------------------------
(レガシー改善に関しては文章が長くなるのでいつもと違って、〜だ、〜である口調で書きます。)
CakePHPははじめてなのでところどころハマったが基本的なCRUDは出来た。というかbakeで殆ど生成されるのだが。CakePHP1.3のbakeはデフォルトでページングとカラム毎のソート機能が入っている。
しかし、「データがたくさんある時ページ分けして欲しい」というタスクが幾つかRedmineにあったし、ソースをみても既存の部分はbakeを使った形跡は無い。俺が初bakeか。
bakeにともなってi18nも使った(bakeで生成されるソースにはi18n前提だし)。現状、poファイルが無い。俺が初i18nか。
% cake bake i18n extract
こんな感じでソースからpotファイルを生成する。app/vendorsにPHPExcelというプラグインがあって、それの処理に死ぬほどメモリと時間がかかる。app/vendorsを除外する方法は無いものか。
ソースを見ると上記にpotを既存poにマージする機能があるっぽいが、やり方がわからなかったのでとりあえず初回はmsginitした。
macではデフォルトでgettextが入っているが、% brew link gettext
しないとパスが通らないところが若干罠だった。
既存ソースに何故か
__('')
msgid ""
msgstr ""
みたいな空文字を翻訳している箇所が5箇所ぐらいあってmsginitが出来ない。どういう理由でこういうコードがあるのか不明だが、とりあえず消した。
modelでvalidateエラーメッセージをi18nしようとしたがsyntax errorが出る。何故?と思ったがphpではrubyと違ってインスタンス変数の定義は定義であって評価じゃない。初期値は書けるが関数の実行はできない。
要は下記はOKだが、
class User {
public $name = 'komagata';
}
下記は駄目ということだ。
class User {
public $length = strlen('komagata');
}
rubyを始めた頃はattr_accessorを見て「クラス定義内も普通に式が評価されるのか、凄いなー!」なんて思ったはずだが、それが当たり前になりすぎてそうじゃない言語が沢山あることを忘れていた…。(array()はOKなのでarrayは関数じゃなくてリテラルなのかな?)
class RegularOrder extends AppModel {
public $validate = array(
'interval' => array(
array(
'rule' => 'numeric',
'message' => __('must be number', true)
)
)
);
}
何故これがsyntax errorなんだー!・・・みたいな。
CakePHP1.3のbakeが生成するコードはFormHelperを使っている。railsのscaffoldと比較して疑問点がいくつかあった。
bakeが生成するviewのコードは下記のようになっている。(分かりやすく色々端折ってます)
<?= $form->input('interval') ?>
でも実際は下記のようにモデル名も書かないと動かない。
<?= $form->input('RegularOrder.interval') ?>
formはmodelを知ってるんだから自動で付けてくれてもいい気がするが、PHP4もサポートするせいでモデル名がわからないのかもしれない。それだったらPHP4サポートを切ったCakePHP2系だったら改善されてるのかも。
既存のレガシーの中にリーダブルコードを書いていく為のショーモナイテクニックに勝手に名前を付けていきたいと思います。
既存のテンプレートの中の共通化したい部分はこんな感じで書かれてる。
<body> <?php include('../views/head-menu.ctp');?> <h2 class="title_1">注文入力</h2>
ファイル名と置き場所が分かり辛い。include(!)ではなく、CakePHPのElementを使って欲しい。かといって既存部分をいじったら他で何が起こるか分かったもんじゃない。怖い。
app/views/elements/admin/menu.ctp:
<?= $this->element('../head-menu') ?>
そこでこんな感じで本来置きたい場所に一旦既存のテンプレを読み込むだけのProxy的なElementを作る。そうすると、新しく実装するviewはリーダブルでいられる。
app/views/layouts/admin.ctp:
<?= $html->doctype('xhtml-trans') ?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<?= $html->charset() ?>
<title><?= $title_for_layout ?></title>
<?= $html->css('admin') ?>
<?= $scripts_for_layout ?>
</head>
<body>
<?= $this->element('admin/menu') ?>
<div id="content">
<?= $content_for_layout ?>
</div>
</body>
</html>
同じく、既存のCSS(style.css)はゴチャゴチャなので新しく作るview用のCSSはリーダブルにしたい。そこでこう!
app/webroot/css/admin.css:
@import url('style.css'); /* ここに新しいviewで必要なcssを書いてく */
レガシーCSSとリーダブルCSSを切り分けることが出来る。
僕と一緒にレガシー改善をしてくださるプログラマーを募集しています。常駐・受託、開発会社・フリーランス、派遣・バイト問いませんので是非お願いします。というか助けて欲しい・・・。詳しくはこちら。
日本語ファイル使ったことなかったけど、レガシー改善でsvnからgitへ移行したら日本語ファイル沢山。
Command Line Toolsで入ってるgitは若干古い。
% git version
git version 1.7.10.2 (Apple Git-33)
1.7.12から日本語ファイル名(UTF-8-MAC)が扱えるようになってるのでbrewで入れる。
% brew info git
git: stable 1.7.12.1, HEAD
% brew install git
実験的な機能という位置付けらしいのでデフォルトではオフになってる。オンにする。
% git config --local core.precomposeunicode true
マシン全体でオンにしたい場合は下記。
% git config --global core.precomposeunicode true
怖話でcomment付ける対象なんて"怖い話"だけに決まってるじゃん!
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.text :body
t.references :story
t.timestamps
end
end
end
余裕過ぎワロタwww
1年後・・・
"怖い漫画"・・・だと・・・!?
\( ^o^)/ オワタ
じゃ、じゃあ、comic_commentsというテーブルを・・・(レガシー直行)
でも大丈夫、後からでもcommentableに移行できます。今回はacts_as_commentableを使いました。
# Gemfile:
gem 'acts_as_commentable', '2.0.1'
% rails g comment
modelとmigrationでファイルを上書きしそうになるけど上書きしない。
# app/models/comment.rb:
class Comment < ActiveRecord::Base
include ActsAsCommentable::Comment
belongs_to :commentable, polymorphic: true, counter_cache: true
end
# app/models/story.rb:
class Story < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
acts_as_commentable
attr_readonly :comments_count
end
# app/models/comic.rb:
class Comic < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
acts_as_commentable
attr_readonly :comments_count
end
polymorphicかつcounter_cache使う時は、相手先をreadonlyにしとく必要があるみたいです。
class AddCommentableToComment < ActiveRecord::Migration
def up
rename_column :comments, :story_id, :commentable_id
add_column :comments, :commentable_type, :string, default: 'Story'
add_index :comments, :commentable_id
add_index :comments, :commentable_type
end
def down
rename_column :comments, :commentable_id, :story_id
remove_column :comments, :commentable_type
end
end
これで既にコメントが沢山あってもcommentableへの移行がバッチリ。この調子でtag, category, voteもどんどんやっていこう!
cakeディレクトリがcake_core/cakeという感じで置かれていて色々設定しないとcakeコマンドが動かないのでデフォルトの位置に戻した。おそらくcakeコマンドによるgenerateは使ってなかったんだろう。generateを使わないとCakePHPのレールから大幅に外れることになりがち。cakeコマンドが動いたことによってようやくmigrations pluginを導入できた。現状のDBの状態をmigrationファイルに変換できたが、そもそも初期データはどうあるべきなのかがわからないので困った。テーブルが50ぐらいあるDBの初期データを手で作っていたら日が暮れてしまう。相談したところ、初期化に使っているSQLがあるとのことでそれを頂いた。とても助かった。
seleniumでテストを書いている。とにかく「このページ開いたらエラーが出てるんですけど」という状態を無くしたい。capybaraとかであれば「GETしてステータス200かどうかだけを調べる」というのを量産すれば可能だけどseleniumでステータスが取れるかどうかがわからない。そもそもPHPでWARNINGとかが発生した時ってHTTP的には普通に200をかえしちゃったりしてないだろうか?試してみよう…。(どなたか知ってる方がいたら教えていただけるとありがたいです)
@hrysdは引き続きバグ修正。数日前にPHPはじめたのに頑張っている。しかし元々ソースがカオスなので一箇所修正したと思ったら、その影響で他のどこかが壊れるということが多発する。一刻も早く自動テストを増やさねば。
バグ修正をしているとき、下記の用な感じで変数名やカラム名が全然意味がわからないのでわかったところにコメントを入れてた。変数名や関数名を分かりやすくしてあればこんな作業は必要無いのに…。カラム名のD1, SD, MT, YDとかまったくわからない。なぜカラム名の長さの数バイトをケチるのにsessionにバカでかいデータを入れまくるのか。
$dd = $tk[0]['NcrmDTeiki']['D1']; // 発生日付(YYYYMMDD) $dd = $tk[0]['NcrmDTeiki']['SD']; // 配送予定期間(開始日付)(YYYYMMDD) if ( $tk[0]['NcrmDTeiki']['MT'] == 3 ) { if ( $tk[0]['NcrmDTeiki']['SD'] > 0 ) { $dd = $tk[0]['NcrmDTeiki']['SD']; } } $kyou = date('Ymd'); $kyou = $chd[0]['NcrmDCyuumon']['D1']; if ( $lst != 0 ) { $kyou = $lst; } // 次の日付を求める $yyy = substr($dd,0,4); $mmm = substr($dd,4,2); $ddd = substr($dd,6,2); if ( $mmm == 4 or $mmm == 6 or $mmm == 9 or $mmm == 11 ) { if ( $ddd == 31 ) { $ddd = 30; } } if ( $mmm == 2 ) { if ( $ddd >= 29 ) { $ddd = 28; } } if ( $tk[0]['NcrmDTeiki']['MT'] == 1 ) { $mmm = $mmm + 0; if ( $mmm == 13 ) { $yyy = $yyy + 1; $mmm = 1; } $ddd =$tk[0]['NcrmDTeiki']['YD']; }
このブログでも募集文を書いているが、社長も開発会社を探してくれている。目安として、"作業担当者のGithubアカウントもしくは見せられるコードがあること"を条件に探しているんだが、今までの5〜6社で該当するところが一つもない。普通そういうもんなんだろうか。下記のようにGithubでPHPのrepos持ってて、Tokyo在住の人のブログとかをみて、開発を受け付けているか探したほうが早いんじゃないかと思った。
ECのシステムで定期購入という機能がある。一回買うと定期的にものが送られてくる機能だ。この機能の実装がバグが多発(というかまだできてない)していて困っている。
実装的にちょっとバグが出ているなら直せばいいんだけど、仕様の段階で破綻しているっぽいので悩む。
定期購入を実現するために今の仕様では、購入した時点で普通の商品と同じように1回目の伝票を1個作成するのだが、定期購入は期間をしていして、2回目、3回目、4回目などの未来の伝票を画面から手動で生成するようになっている。
伝票は編集したり削除したりできるので2, 3, 4回目が出来た後で1回目(元伝票と呼んでいる)を変更するとおかしな自体が多発する。2回目だけを消したら3回目は本当に3回目なのか?とか、2回目を消した後で次の伝票を作成(5回目を作成したいつもり)するとまた2回目が出来たり3回目が2個出来たり…。
そもそも手動で伝票を生成するのなら定期購入って意味あるのか?というのはあるにしろ、定期購入というのは実物の伝票ではなく、購入に関するルールという抽象的な概念なので、実物の伝票として未来まで作成してしまう(DBにデータを作成してしまう)とルールの変更があった時に不整合がいかにも起こりやすいことはさらっと聞いただけでわかる。
今のシステム(新CRM)の前身である旧CRMはそういった不安な仕様はなく、定期購入は先に未来の伝票を何回目まで先にデータを作るといったことは無い。
締め切りが迫っている中、現状をなんとか改善するのか、仕様を見直すのか、リスケするのか、答えが出ていない。現状を改修しようとしても何らかのトラブルが起きそうなことは目に見えている。それを是とするのか非とするのか、当然非なんですが、答えのでない長時間の会議に僕も疲れてしまってとりあえず現状維持みたいな感じになってしまいました。
そういうのを是とすると、中長期的には、開発チームとして論理的矛盾やバグがあってもいい・仕方無いみたいな雰囲気が根付いてしまう。根付いた文化を変えるのはとても大変なので急に「次のバージョンからは安定したシステムを作ろう!」といってもそういった下地ができていなければ無理。まずその辺の認識を変えていかなければ…。
チンカスプログラマーことhrysdがバイトで来てくれることになったので一緒に大門に出社。
初出社前に既にバグを一個潰してコミットしているという荒業を見せたhrysdだが、3000行を超えるcontrollerに早くも心を折られる。
俺「actionのメソッドが5行を超えたら危険印、なんていうrailsのぬるま湯に使ってたんだよ!これがサバンナだ。」
出社後早速、社長にGithub Organization Bronzeプランを契約してもらう。技術的なことはわからないというが、リスクを背負って立てなおそうという気持ちが伝わって来ました。
9月30日の直近の締め切りに間に合わないのでsvn + redmineからの移行は10月にお預けだ。
まずはsvnでもトップにぶち撒けられてるというのは辛いのでtrunkディレクトリを作ってそこにsvn mv。しかし、svn updateの使い方がわかってない外部パートナー様からヘルプの問い合わせ。svn updateしてからsvn ciすればいいだけだと思うんだが・・・。
社長が検索で見つけた2社様と打ち合わせ。技術的に良い会社かどうか判断して欲しいとのこと。githubアカウントの提出をお願いしたら(担当者がプライベートで作ってるコードでも構わないので)2社とも無いとのことで会う前からちょっとガックシ。
1社様はunittestを書いたことがあるということで無くは無いといった感じ。(パートナー様を選ぶ最低限のポイントとしてはgitが使えること、自動テストが書けることの2点と伝えてあります。できればプライベートでコード書いてる人が良い)もう一社様はテストが書ける人を探してると言ったら、エクセルのテスト仕様書を見せられた。phpunitやselenium, jenkinsなんて名前も聞いたこと無い様子だったのでちょっと一緒にやってくのは厳しそうでした。(社長には良い感じの人に見えたと言っていたのが印象的でした。やはりプログラマーが見るべきだと改めて感じた。)
ステージング環境をさくらVPSで用意。jenkinsでのsimpletest, phpunit + selenium rcでのCI、capistranoでの継続的デリバリーの環境が整った。後はテストを増やすだけだ。
何はなくとも自動テストだ。テストが無いと、バグもデグレも治った保証が無いし、怖くてリファクタリングできない。
最低限のテストを完備し、その庇護の元、デグレに怯えないバグ修正やリファクタリングを行なっていく。それにはバグを再現するためのテストデータの整備が欠かせない。要はmigrationの仕組みが無いと、多くのバグがそのタイミングでたまたま発生しただけで再現しないため放置されるのだ。(CakePHP1.3でのmigration pluginの導入はまだうまく行ってない)
CakePHP1.3からCakePHP2.xへの移行はもうファイル名やフォルダの命名規則、コア関数などが変わりすぎていてほぼ無理だと判断した。
かと言って中長期的な施策ばかりで直近のデッドラインを無視するわけには行かないので、同時にhrysdにお願いしてヤバイ順に泥臭いバグ修正を並行して行なってもらった。午後は僕も参戦した。
CakePHPのユーザー認証コンポーネントを使ってない。before_filter的な機能を使ってないので全Controllerの全メソッドの先頭にユーザー認証処理が書いてある。
ユーザーの権限管理にCakePHPのACLを使っていない独自実装の為に膨大な行数が直書き+コピペされている。
検索条件、ページ間のデータ受け渡し(本来POSTされるべきもの)、様々な構造化されたデータのやり取りは全てSessionを通して行われているので各controller、method間の入出力が非常に分かり辛い。完全にGUIアプリのようなステートフルな実装になっているので単体でのテストが非常にやり辛い。バグの再現も難しい。
foreachで処理できるところを(ループをインライン展開しているのか?)全て手で書下しているのでただ代入するだけのような同じ処理が延々と何十行と続く。
$pg, $md, $mk, $sw, $sidなど何を表しているのかわからない変数が大量にあり、推理に時間がかかる。($mdがmodeの略だとわかった時にはガッツポーズをした)
validationはサーバーサイド(model)には一切無く、javascriptだけで行われている。これではデータの整合性を保つのは難しい。
バグを減らす一番の方法はコードの行数を減らすことだ。コード行数を減らすというと、共通処理を関数にくくり出すなどをまずイメージしがちだが、真っ先に考えるべきは、既存のライブラリやフレームワークが備える機能をなるべく使うということだ。
十分テストされたライブラリはその場で作ったスクラッチコードより品質が高い。多くの目に触れているものは不具合の解消も進んでいるだろう。
作ろうとしている機能が、既にあるライブラリで解決可能かどうか、フレームワーク標準の機能にそういったものが無いか、吟味するのはとても重要だ。(フィットするものが無ければその時初めて作ればいい)
そういった調査をせず、闇雲にオレオレコードを量産するとバグ発生率が増加する。
技術的負債が非エンジニアにとって見えづらいのは、組み合わせ爆発の恐ろしさを理解していないことにあると感じました。
日常生活では仕事が増えるといっても精々2倍になったとか3倍になったで大騒ぎ。ソフトウェアの分野では浅慮によって手間が2乗、3乗という風にグラフにしたら垂直に上昇していくようなことが簡単に起きる。
階乗とは違うけどわかり易い例でいうと、何かのmodelに対してcommentを付けたいという要求があったとする、railsノリで言えば、acts_as_commentableみたいな作りにしてcommentテーブルは共通、commentablesみたいなテーブルを作って、コメントを付けたい対象が増えても組み合わせは1×Nのまま爆発的な増大はしない。
ところが、ベタに作ると、post_comments、page_comments、picture_commentsといった風にドンドン増えていく。
色んなモノにtagを付けたいという要求があったらまたpost_tags、page_tags、picture_tagsとその分だけテーブルが増える。
comment, tagの部分もcategory, review, ratingと増えていくとN×Mで爆発的にテーブルは増えていく。
そういったちゃんと抽象化せず、場当たり的実装を重ねた結果、DB定義書のエクセルシート数が3桁に迫り、そんなドキュメントを人間の手でメンテナンスするのは事実上不可能になり破綻する。破綻したドキュメントは軽んじられ、作成者は疲労し、メンバー全員のモチベーションは地に落ちる。
いくら気合で頑張るといっても1日は定時8時間の3倍の24時間しかないのだ。物理的に3倍以上の作業量をこなすことは出来ない。組み合わせ爆発で3倍なんて簡単に超えてしまう。
いかに組み合わせ爆発を起こさないような作りにするか。それが肝心なのだ。
非エンジニアの方(経営者など)はこの動画をみて是非とも組み合わせ爆発の恐ろしさを感じて欲しい。
僕が参加しているプロジェクトでは短期でも一緒にレガシー改善に正面から取り組んでくださるプログラマーさんを募集しています。フリーランス・開発会社様、アルバイト、どんな形でも結構ですのでご連絡いただけるとありがたいです。詳細はこちら