フォーム読み込み中
2022年に新卒入社した金です。主に社内向けシステムやMSPサービスの開発・改善、お客さまのクラウド環境構築に携わっています。開発の面では、社内向け業務アプリケーションのFront-end、 Back-end、 CI/CDを経験しています。
本記事では、非情報系でプログラミングの経験もほぼ皆無だった私が、この1年間で学んだ経験をまとめました。
開発に興味を持っている方を対象に、開発駆け出しの人が陥りがちなことと、その対策を紹介していきます。後半の例はPythonを用いて記述しますが、他のプログラミング言語でも活かせるように詳細を解説します。
開発初心者が1番やっていけないのは、他の人のコードを読まないことだと思います。
他の人の優れたコードにあまり接しないと、自己流のコードになりかねません。その結果、数ヵ月後の自分自身すら読めないコードを書くことにつながります。また、より優れた書き方・簡略な書き方に触れるチャンスがなくなり、自分のコードを改善することが難しくなります。
他の人のコードを読まない人は、2つのパターンに分けられます。1つ目は、自分自身で頑張ればなんとかなると思い、一人で頑張ってしまうタイプ。2つめは、他の人のコードは見ているがコピーするだけで、内容やその人の考えには興味を持たないタイプです。
最初は、私も他のコードを読まずに、全て自分自身で頑張って実装するタイプでした。プログラミングの勉強の第一歩としてはとてもよい方法だとは思いますが、先輩から違和感が多いといつも指摘してもらって何度も書き直すことになったのが記憶に残っています。
その後、一時期は他の人が書いたコードをそのまま利用するタイプになりました。あまり考えずに先輩のコードをそのまま利用することで、違和感をなくしていたのですが、なぜこのコードはこうなっているかを聞かれても答えることができませんでした。
私は以下のようなことを意識することで解消することができました。
開発者がやってはいけないことの2つ目は、スコープを広げようとしないことです。特定技術や言語にこだわり過ぎると、ある部分に特化しすぎてしまうことがあります。機械学習の過学習みたいな感じですかね。
スコープを広げないのは心の障壁が原因だと思います。その障壁にはいろんな種類があると思いますが、大きく分けると母国語以外の言語と新しいことへの拒否感があると思います。
私の場合も最初は母国語である韓国語以外では検索しない癖と、業務で最初に触ったクラウドであるAzureにこだわる癖がありました。
母国語のみで検索することで、素早く内容を理解することは可能でしたが、私がほしい情報や必要な情報がない場合が多々ありました。また、Azureでの作業時に、azコマンドでリソースの一括削除ができないのが当たり前だと思い、手で削除作業を行なっていました。
私は、ほしい情報がなかったことを機に英語と日本語でも検索をするようになりました。そのおかげで、3ヵ国語を開発に活用することができ、必要な情報を的確に探すことができるようになりました。クラウドの方も、Amazon Web Services、Google Cloudを少し触ってみることで一括削除がないことは不便であると気づき、その解決のための業務に携わることもできました。
思いっきり踏み入れてみること、慣れはじめたら少しずつ違う分野にもアンテナを貼ってみることが大事だと思います。
以前はファイル一つに全ての機能を書き込むことが多々ありました。心のどこかで、長いコードを書ける人はすごいと思っているのも影響があったかと思います。先輩に私が書いたコードを見てもらって褒めてもらえると思いましたが、そんなことはなかったです。
長いコードを避けるべき理由はなんでしょう。長過ぎるコードは読みづらくなります。コード全体の把握が難しくなり、流れを追うのが大変になります。読む気が失せてしまいます。その結果、コードのレビュアーの方々にも影響し、よいレビューをもらいにくくなります。
例として特定の期間のAzureの利用料金を取得して、総額をSlackにwebhookで送信するコードを書いてみます。一部省略しています。
import ...(適切なライブラリ)
def main():
# Credentialを取得する
subscription_id = '<Azure Subscription ID>'
credential = DefaultAzureCredential()
client = ConsumptionManagementClient(credential, subscription_id)
# 日付の範囲を設定する
end_date = datetime.now().date()
start_date = end_date - timedelta(days=days)
date_range = DateRange(start_date=start_date, end_date=end_date)
# トータルコストを算出する
usage_details = client.usage_details.list(
date_range=date_range
)
# コスト情報を集計する
total_cost = 0
for usage_detail in usage_details:
total_cost += usage_detail.cost.amount
# Slackにメッセージを送信
message = f'Azureの{start_date}から{end_date}のコストは、{total_cost}です。'
webhook_url = '<Slack Webhook URL>'
payload = {'text': message}
response = requests.post(webhook_url, json=payload)
if response.status_code == 200:
print('Slackにメッセージを送信しました。')
else:
print('Slackにメッセージを送信できませんでした。')
単純に総額をSlackに通知する機能の実装ならば、これで十分です。ですが、ここで他の機能や対象の拡大(総額以外の情報を取得したい、他のサブスクリプションも対象にしたいなど)を行うと、だんだんコードが長くなります。コードが長くなっていくことはどう解決すればよいでしょうか?
キーになる考えは、コードの中で再利用できる部分を分離することです。この考えを実現するには、関数化、モジュール化、ファイルの分離、インスタンス化のような方法があります。
私としては、上記のコードのCredentialを取得する部分、コストの情報を集計する部分、Slackへの送信に関する部分は、他の機能の実装や対象の拡大の際に同じようなコードを書く必要があると思います。ここは再利用を考え、別の関数として分離すると、
import ...(適切なライブラリ)
def get_crednetials(subscription_id):
...(省略)
def calculate_total_cost(usage_details):
...(省略)
def send_slack_message(start_date,end_date,total_cost):
...(省略)
def main():
# Credentialを取得する
subscription_id = '<Azure Subscription ID>'
client = get_crednetials(subscription_id)
# 日付の範囲を設定する
...(省略)
# コスト情報を取得する
usage_details = client.usage_details.list(
date_range=date_range
)
# トータルコストを算出する
total_cost = calculate_total_cost(usage_details)
# Slackにメッセージを送信
send_slack_message(start_date,end_date,total_cost)
みたいになり、mainの関数が短くなります。こうなると、mainの作業の流れがわかりやすくなります。詳細が気になる場合は関数の中身で確認できます。
これらを別のモジュールまたはファイルで分離すると、
from .my_library import get_crednetials
from .my_library import calculate_total_cost
from .my_library import send_slack_message
...(適切なライブラリ)
def main():
# Credentialを取得する
subscription_id = '<Azure Subscription ID>'
client = get_crednetials(subscription_id)
# 日付の範囲を設定する
...(省略)
# コスト情報を取得する
usage_details = client.usage_details.list(
date_range=date_range
)
# トータルコストを算出する
total_cost = calculate_total_cost(usage_details)
# Slackにメッセージを送信
send_slack_message(start_date,end_date,total_cost)
みたいになります。対象の拡大や新たな機能の追加の際にも、関数の読み込みによって対応でき、より簡単に実装できます。また、関数を修正・改善する際は修正箇所が少なくなり、修正改善がやりやすくなります。
インスタンス化の場合は、上記の関数をクラス内部で定義することで同じような実装ができます。
from .my_library import get_crednetials
from .my_library import CostInformation
...(適切なライブラリ)
def main():
# Credentialを取得する
client = get_crednetials(subscription_id)
# インスタンスを生成する
my_cost = CostInforamtion(client)
# 日付の範囲を設定する
...(省略)
# コスト情報を取得する
my_cost.fetch_cost_inforamtion()
# トータルコストを算出する
my_cost. calculate_total_cost()
# Slackにメッセージを送信
my_cost.send_slack_message(start_date,end_date)
関数を用いるか、インスタンスを用いるかは、各々の長短があります。各自判断してみてください。
配属直後の私は、あまりセキュリティに関しての意識が高くなく、ID情報のようなシークレットの管理に慣れていませんでした。コードの中にID情報を記入してしまうことがありました。
ローカルでの検証では特に問題にはなりませんが、書いたコードを社内外で公開することになると情報漏えいのような大きな問題になります。公開するつもりではなかったとしても、GitHubにpushしてしまったりすると、事故につながることがあり得ます。重大なセキュリティインシデントに繋がりかねないため、この点は強く指導を受けました。
情報漏えい以外にも、どんなパラメータだったかの把握に時間がかかることや、他のプロジェクトや対象の変更のためのコードの修正が頻繁になる問題があります。
...
# Credentialを取得する
creclient = get_crednetials("00000000-0000-0000-0000-000000000000")
...
message = 'Azureの' + '00000000-0000-0000-0000-000000000000' + f'の{start_date}から{end_date}のコストは、{total_cost}です。'
...
上記のようなコードを書くと、途中で「あれ?この値なんだっけ?」になることや、情報漏えいに気をつけないといけなくなります。
この問題を解決する方法は、環境変数(envirionmentまたはcontextなど)を設定することです。外に出しては行けない値は.envのようなファイルまたは、各サービスのシークレットとして設定をしておくことで、外部への漏出を防ぐことができます。
また、後で紹介しますが、2度以上登場する変数は名前をつけて定義することで、値の正体がわからなくて困ることはなくなります。
import os
...
# 環境変数を読み込む
SUBSCRIPTION_ID = os.environ.get("SUBSCRIPTION_ID")
...
# Credentialを取得する
client = get_crednetials(SUBSCRIPTION_ID)
...
message = f'Azureの{SUBSCRIPTION_ID}の{start_date}から{end_date}のコストは、{total_cost}です。'
...
環境変数としてSUBSCRIPTION_IDを読み込むことで、00000000-0000-0000-0000-000000000000という値がなんの値か悩む必要もなく、コードもより簡潔になります。また、設定した環境以外でこのコードを活用したい場合は、環境変数のファイルのみを新しく作成すればよいので楽にコードを利用することができます。
このように、ハードコーディングの代わりに環境変数を設定することはセキュリティの面でも、コードの質の面でもよいということがわかります。
ただし、環境変数を設定する際は、環境変数の管理には気を付ける必要があります。うっかり環境変数を変えないことで、コードが機能しないことから情報漏えいまで、さまざまなインシデントが発生する可能性があります。シークレットの取り扱いには十分気をつけましょう。
動けばどうでもよいと思っていた学生時代の私は、変数の名前を適当につけていました。例えば、a,b,cやx, y, zやa-1, a-1-12みたいな感覚です。また、2,3回出たくらいでは特に変数として定義せず、ハードコーディングして使っていました。命名規則ということを全く意識しなかった結果、以下のようなコードになりました。
...
# Credentialを取得する
c1 = get_crednetials('00000000-0000-0000-0000-000000000000'
)
c2 = get_crednetials('11111111-1111-1111-1111-111111111111')
...
m1 = 'Azureの' + '00000000-0000-0000-0000-000000000000' + f'の{a-1}から{b-1}のコストは、{x-1}です。'
m2 = 'Azureの' + '11111111-1111-1111-1111-111111111111' + f'の{a-2}から{b-2}のコストは、{x-2}です。'
...
当然ですが、他の人、いや数ヶ月後の私自身すら書いたコードを理解するには長い時間が必要でした。変数名からなんの情報も得られず、意味を理解するのに時間がかかりました。
変数名を適切に付けるためには以下のようなことを意識する必要があると思います。
これらを意識して、変数名を付けることで、上記のコードは以下のようになります。
...
# 環境変数を読み込む
SANDBOX_SUBSCRIPTION_ID = os.environ.get("SANDBOX_SUBSCRIPTION_ID")
STAGING_SUBSCRIPTION_ID = os.environ.get("STAGING_SUBSCRIPTION_ID")
...
# Credentialを取得する
sandbox_client = get_crednetials(SANDBOX_SUBSCRIPTION_ID)
staging_client = get_crednetials(STAGING_SUBSCRIPTION_ID)
...
sandbox_message = 'Azureの' + SANDBOX_SUBSCRIPTION_ID + f'の{start_dat}から{end_date}のコストは、{sandbox_total_cost}です。'
staging_message = 'Azureの' + STAGING_SUBSCRIPTION_ID + f'の{start_dat}から{end_date}のコストは、{staging_total_cost}です。'
...
特に命名規則のことは慣れるまで意識するようにしてください。次は命名規則の例です。
camelCasesnake_casekebab-casePascalCase(UpperCamelCase)ちなみにPythonでは、PascalCaseとsnake_caseが利用されていて、以下のような命名規則が推奨されています。
命名規則 | 使用範囲 | 例 |
|---|---|---|
全て小文字 | パッケージ |
|
全て大文字 (アンダースコア) | 定数 |
|
snake_case | 変数名、関数名、モジュール名 |
|
PascalCase (UpperCamelCase) | クラス名、例外、方変数 |
|
最後に紹介する癖は、インデントを意識しない癖です。Pythonの場合はインデントを守らないと構文エラーとなるため、意識しなくても大きな問題はなかったですが、エラーを出してくれない言語では適当に書いていました。幸いにも優しい先輩の方々に巡り合え、変な癖がつく前にインデントを意識するようになりました。インデントを意識しないとコードの構造が読みにくくなることや、エラーを出すこと、意図通りに動作しないことが起こり得ます。
簡単かつ確実な方法は、よく使用するエディタにインデントをチェックしてくれるformatterやlinterを導入することです。formatterはコードの見た目を揃えるツールであって、インデントやスペース、改行など、コードの整形を行ってくれます。linterの場合は、コードがコーディング規約に従っていない警告を出すなど、コードの検証を行ってくれます。
本記事を最後まで読んでいただき、ありがとうございます。本記事では開発駆け出しの私が1年間で学んだことを紹介しました。
他にもいくつかありますが、内容が長くなりすぎて全てを書くことはできませんでした。本記事の反響次第では学びも執筆するかもしれません。記事を書きながら、私が過去に書いたコードを読んだり、先輩に指摘された記憶をたどりました。その内容を書くのは少し恥ずかしかったのですが、自身のための振り返りにもなりました。
今回の内容が読者の方々のコードの可読性や保守性の向上につながり、役立つことを願っています。
条件に該当するページがございません