RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【オフショア】ベトナムメンバと理解する「PHPリーダブルコード」 〜第2回 ループとロジックの単純化〜

【オフショア】ベトナムメンバと理解する「PHPリーダブルコード」 〜第2回 ループとロジックの単純化〜

"リーダブルコード" ベトナム語解説の第2回です。
ベトナムとのオフショア開発において可読性/保守性が高い開発が行えることを目的にして、 "リーダブルコード" やその他書籍、普段の経験を参照し、開発におけるテクニックをまとめました。*1
この記事をベトナムチームのメンバに読んでもらうことで、"リーダブルコード" の知識が日本チームとベトナムチームの共通認識となり、コード品質が向上することを目的としています。

本稿は「ループとロジックの単純化」について解説します。

Đây là phần thứ 2 của loạt bài giải thích "Readable Code" bằng tiếng Việt. Trong bài post này, tôi sẽ tóm tắt nội dung của "Readable code" và giải thích bằng cả tiếng Nhật và tiếng Việt, để có thể sử dụng trong việc phát triển Offshore với Việt Nam. Khi team Nhật Bản và team Việt Nam đọc bài viết này, sẽ có cách hiểu chung về kiến ​​thức "Readable code" giữa các team và hướng tới mục đích là để nâng cao chất lượng program.

Dự định tổng cộng là có 3 chương, và bài post này là Chương 2, giải thích về "Đơn giản hóa Loop and Logic".

ベトナムメンバと理解する「PHPリーダブルコード」
〜第2回 ループとロジックの単純化

PHP Readable code”hiểu được cùng với member Việt Nam
〜Chương 2 Đơn giản hóa Loops and Logic〜

はじめに / Lời nói đầu

先日はオフショア先であるベトナムのメンバ向けに、「表面的な改善」についてまとめました。
前回はコメントや命名などに関する内容でしたが、今回は実際に記載されているロジックに関する内容になります。

Hôm trước, tôi đã tổng hợp về "Cải thiện giao diện" của readable code, dành cho member VN là Offshore. Lần trước là về comment và naming, nhưng lần này là về logic được viết trong thực tế.

これらのテクニックは、自分がコードを書く時だけでなく、既存のコードを読むときにも役に立ちます。
既存のコードがどうしてこのように記載されているのかを理解し、昔の実装者の意図を理解した上で修正を加えることで、保守性に優れたコードを維持することができます。

これらのテクニックを身につけて、保守性の高いコードを目指しましょう!!

Những kỹ thuật này không chỉ hữu ích khi bạn viết code mà còn khi bạn đọc code hiện có. Bạn có thể duy trì good code có tính bảo trì, bằng cách hiểu tại sao code hiện tại được viết như thế này, hiểu ý đồ của những người implment hồi trước và sau đó thêm chỉnh sửa.

Hãy cùng trang bị những kỹ thuật này và hướng tới code có tính bảo trì cao nhé!!

◆ 関連ブログはこちら
tech-blog.rakus.co.jp

この記事の使い方 / Cách sử dụng bài viết này

チーム内の共通言語として / Như là ngôn ngữ chung trong team

  • 各テクニックを日本語とベトナム語の両方で記述しています
  • 各テクニックには、英語での名称をつけています
    • 日本もベトナムも使える英語のテクニック名があることで、フィードバック時に指し示しやすくなります
    • 英語名は原著をもとに、簡易な単語を利用しています
  • フィードバックや議論時に、この記事のリンクを貼るとさらに伝わりやすくなります

  • Mô tả từng technique bằng cả tiếng Nhật và tiếng Việt

  • Từng technique sẽ có đặt tên bằng tiếng Anh
    • Khi có technique name tiếng Anh mà phía Nhật và Việt Nam đều có thể sử dụng, thì sẽ dễ dàng chỉ ra hơn khi feedback
    • Tên tiếng anh sẽ dựa trên bản gốc và sử dụng từ đơn giản
  • Khi feedback hoặc thảo luận, nếu gắn link của bài viết này thì sẽ dễ truyền đạt hơn

Sample 1:

[Japanese]

この条件の場合、これ以上処理は行われません。そのため「3-1. Early Return」にしてください。

[Vietnamese]

Trường hợp là điều kiện này, các xử lý hơn nữa sẽ không được thực hiện. Do đó, hãy chọn "3-1.Early Return".

https://tech-blog.rakus.co.jp/entry/ReadableCodeWithVietnam02#3-2-Early-Return

Sample 2:

[Japanese]

この変数にはintegerしか入らないはずですので、厳密な比較を使ってください。

詳しくはこちらの「2-2. Strict Comparisons」を参照してください。

[Vietnamese]

Vì các biến này chỉ chứa các số nguyên, nên hãy sử dụng các phép so sánh nghiêm ngặt.

Để biết thêm chi tiết, hãy tham khảo ”2-2. Strict Comparisons” ở đây.

https://tech-blog.rakus.co.jp/entry/ReadableCodeWithVietnam02#2-4-Strict-Comparisons

ベトナムでの学習資料として / Được xem như là tài liệu học tập ở Việt Nam

  • ベトナム語に翻訳しているため、ベトナムの方でも読みやすいはずです
  • プロジェクトに参画したベトナムメンバの学習資料として利用できます

  • Vì được dịch sang tiếng Việt nên có lẽ sẽ dễ đọc ngay cả với người Việt Nam

  • Có thể sử dụng làm tài liệu học tập cho các thành viên Việt Nam đã tham gia dự án

リーダブルコードについて / Về Readable Code

www.oreilly.co.jp

The Art of Readable Code

リーダブルコード より良いコードを書くためのシンプルで実践的なテクニック
Readable Code là Technique đơn giản và mang tính thực tiễn để viết code tốt hơn

有名なより良いコードを書くためのテクニックがまとめられた書籍です。上記の通り、日本語の翻訳本があります。
Đây là cuốn sách nổi tiếng và tổng hợp technique để viết code tốt hơn. Như đã nói ở trên là có bản dịch tiếng Nhật.

1. 変数の使い方に関するテクニック / Các Technique liên quan đến cách sử dụng biến

変数の使い方を変えることで、ロジックはとても読みやすく、バグが入りづらくなります。

Bằng việc thay đổi cách sử dụng các biến, thì logic sẽ trở nên rất dễ đọc và khó bị bug hơn.

1-1. Explaining/Summary Variables

#変数 #命名 #可読性

  • 説明/要約変数
  • Các biến mô tả/ biến tóm tắt

ややこしいコードを変数に入れてしまえば、そのコードが何を表しているのか変数名が説明してくれるため、すぐ理解ができるようになります。
(この時の変数名が値の説明になっていないと意味がないので注意してください。)

Nếu bạn cho code dễ nhầm lẫn vào trong biến, vì tên biến sẽ giải thích là code đó đang biểu thị cái gì nên sẽ giúp ta hiểu được ngay lập tức. (Xin lưu ý rằng sẽ không có ý nghĩa gì nếu tên biến lúc này không giải thích được cho giá trị.)

❌ Bad Code

<?php

if (explode("/", $url)[LAST_KEY_NUMBER] === "root") {
  ...
}

✅ Good Code

<?php

$userName = explode("/", $url)[LAST_KEY_NUMBER];
if ($userName === "root") {
  ...
}

また、変数を使って処理の内容を要約してしまうことでも、読みやすさが向上します。
たとえば、以下のようにあまり大きくない処理でも、結果を変数に入れることによって何を行いたいのかが理解できるようになります。

Ngoài ra, cũng có thể nâng cao tính dễ đọc bằng sử dụng biến để tóm tắt nội dung xử lý. Ví dụ, có thể hiểu những gì muốn làm bằng cách đặt kết quả vào một biến, ngay cả ở xử lý không quá lớn như bên dưới.

❌ Bad Code

<?php

if ($mail->comments[$index]->userId === $session->userId) {
  // User thì có thể edit comment.
  // ユーザはコメントを編集できる
}

✅ Good Code

<?php

$isWritableUserComment = ($mail->comments[$index]->userId === $session->userId);

if ($isWritableUserComment) {
  // User thì có thể edit comment.
  // ユーザはコメントを編集できる
}

1-2. Not Use Useless Variables

#変数 #EarlyReturn #ガード節

  • 役に立たない変数を使わない
  • Không sử dụng các biến vô ích

前述した説明/要約変数は、特に説明せずとも伝わる処理で使う必要はありません。
例えば、以下のように変数にまとめなくてもわかるような処理には使用する必要はありません。

Đối với xử lý mà vẫn truyền đạt được ý nghĩa mà không cần bất kỳ giải thích nào thì không cần sử dụng các biến giải thích / biến tóm tắt đã nêu trên. Ví dụ ở xử lý hiểu được mà không cần tổng hợp vào biến như dưới đây thì không cần thiết phải sử dụng.

❌ Bad Code

<?php

$userId = $session->user->userId;
$updateUserId = $userId;

✅ Good Code

<?php

$updateUserId = $session->user->userId;  // Truyền tải đầy đủ ý nghĩa

他にも、以下の $targetKey のような中間結果を記録するためだけに使う変数も不要なので削除しましょう。
このような中間結果を記録する変数は、コードの理解の助けにはなりません。

Ngoài ra, biến sử dụng chỉ để ghi kết quả trung gian, như $targetKey bên dưới thì cũng không cần thiết nên hãy xóa đi. Các biến ghi lại kết quả trung gian như vậy không giúp ta hiểu được code.

❌ Bad Code

<?php

/**
 * Xóa user đã được chỉ định khỏi danh sách người gửi mail
 * メール送信者リストから指定されたユーザを削除する
 */
function removeUserFromToAddressList (array $addressList, int $removedUserId)
{
  foreach ($addressList as $key => $address) {
    if ($address["userId"] === $removedUserId) {
      $targetKey = $key;
    }
  }

  array_splice($addressList, $targetKey);
  return $addressList;
}

詳しくは後述しますが、以下のように Early Return を行うことで、$targetKey を削除することができます。

Chi tiết sẽ được mô tả sau, nhưng bạn có thể xóa $targetKey bằng cách thực hiện Early Return như sau.

✅ Good Code

<?php

/**
 *  Xóa user đã được chỉ định khỏi danh sách người gửi mail
 * メール送信者リストから指定されたユーザを削除する
 */
function removeUserFromToAddressList (array $addressList, int $removedUserId)
{
  foreach ($addressList as $key => $address) {
    if ($address["userId"] === $removedUserId) {
      array_splice($addressList, $key);
      return $addressList;    // Early Return
    }
  }
}

1-3. Shrink the Scope of Variables

#変数 #スコープ

  • 変数のスコープを狭くする
  • Thu hẹp phạm vi của các biến

このテクニックは非常に有効です。
このテクニックはコードの読みやすさを上げるだけでなく、度重なる変更に強くなり、バグを含みにくいコードを作ることができます。

Technique này rất hiệu quả. Kỹ thuật này không chỉ làm cho code của bạn dễ đọc hơn mà còn làm cho nó có khả năng chống lại các thay đổi lặp lại nhiều lần và làm cho code của bạn ít bug hơn.

変数のスコープを広くしてはいけません!!スコープが広く、寿命が長い変数は、以下のような問題を引き起こします。

  • 変数の値がどこで変更されているかわからなくなる
  • 間違って変数の値を上書きしてしまう危険がある
  • 広い範囲でその変数の値を覚えながらコードを読まないといけなくなってしまう
  • 影響範囲が多くなり、修正が難しくなる

Không được phép mở rộng phạm vi của biến!! Biến có phạm vi rộng và tuổi thọ lâu sẽ gây ra các vấn đề như:

  • Sẽ làm cho bạn không biết giá trị của biến đã được thay đổi ở đâu
  • Có nguy cơ vô tình ghi đè nhầm lên giá trị của một biến
  • Bạn vừa phải đọc code vừa nhớ giá trị của biến đó ở một phạm vi rộng.
  • Phạm vi ảnh hưởng sẽ trở nên nhiều và làm cho việc chỉnh sửa trở nên khó khăn hơn.

グローバル変数を考えるとわかりやすいと思います。
グローバル変数はどこでも読める一方で、どこでも書き換えることができてしまいます。
そのため、グローバル変数を使った処理を記述するときには、コード全体のどこでグローバル変数の値が書き換わるかを調査してから使う必要があり、とても手間がかかります。

一方、スコープが狭い変数であれば、その変数が扱われている範囲をすぐ理解できるため、意図しない値の変更に怯えながら実装する必要はありません。

コードを読むときに考えることをできるだけ減らすためにも、その変数のスコープを狭くしましょう。

Tôi nghĩ sẽ dễ hiểu nếu bạn nghĩ về biến toàn cục (Global variables). Các biến toàn cục có thể đọc ở bất kỳ đâu, mặt khác có thể được viết lại ở bất kỳ đâu. Do đó, khi viết một xử lý đã sử dụng biến toàn cục, cần phải khảo sát xem giá trị của biến toàn cục được viết lại ở đâu trong toàn bộ đoạn code trước khi sử dụng, điều này rất mất thời gian.

Mặt khác, nếu biến có phạm vi hẹp, thì bạn có thể dễ dàng hiểu được phạm vi mà biến đó đang được sử dụng do đó bạn không cần phải lo sợ trước những thay đổi ngoài ý muốn của các giá trị.

Hãy thu hẹp phạm vi của biến để giảm thiểu tối đa những suy nghĩ khi đọc code.

以下の例では、foo()の内部でグローバル変数$dirPathを書き換えていますが、元のコードではそれが見えません。
そのため、グローバル変数$dirPathが書き換えられていることに気づかず、実装者は違うファイルに書き込まれると勘違いして実装してしまいます。

Trong ví dụ sau, biến toàn cục $dirPath được viết lại bên trongfoo(), nhưng bạn sẽ không thể nhìn thấy ở code gốc. Do đó, implementer sẽ không nhận thấy rằng biến toàn cục $dirPath đã được viết lại và sẽ implement với phán đoán sai rằng nó được ghi lại vào một file khác.

❌ ​Bad Code

<?php
global $dirPath;
$dirPath = "/usr/local/system/tmp";

foo();


$filePath = $dirPath . "/data.log";
file_put_contents($filePath, "user:Jonh login");
// Tôi định ghi dữ liệu vào "/usr/local/system/tmp/data.log"
// Trên thực tế, nó được viết vào "/tmp/data.log".
// Bởi vì là đang viết lại $dirPath ở trong foo().
// 
// 
// "/usr/local/system/tmp/data.log" にテータを書き込んだつもりだが、
// 実際は "/tmp/data.log" に書き込まれてしまう。
// なぜなら、foo() 内で $dirPath を書き換えているから。

  :
  :
  :

function foo()
{
  global $dirPath;
  $dirPath = "/tmp";
}

$dirPathグローバル変数にせず、foo()の引数にすることで、$filePathfoo()で利用されていることを明確に示すことができます。

Bằng cách dùng $dirPath làm đối số của foo()mà không biến nó thành một biến toàn cục, có thể biểu thị rõ ràng việc $filePath đang được sử dụng trong foo().

✅ Good Code

<?php
$dirPath = "/usr/local/system/tmp";

$dirPath = foo($dirPath);  // foo()で値が書き換えられていることがわかる/ Có thể hiểu rằng giá trị đã được viết lại ở $dirPath = foo($dirPath);  // foo()

$filePath = $dirPath . "/data.log";
file_put_contents($filePath, "user:Jonh login");

  :
  :
  :

function foo($dirPath)
{
  $dirPath = "/tmp";
  return $dirPath;
}

グローバル変数でなくても、クラス内の、メンバ変数がミニグローバル変数化してしまうことがあります。
以下の例の場合、$status がミニグローバル変数になっており、どこで変更されているかわからない状態になってしまっています。

Ngay cả khi nó không phải là một biến toàn cục, một biến member của một class có thể trở thành một mini global variable. Trong ví dụ dưới đây, $status đang là một mini global variable và bạn sẽ không biết rằng nó đã được thay đổi ở đâu.

❌ Bad Code

<?php
class StatusLogic
{
  private $status;

  private function updateStatus()
  {
    $this->status = "ACTIVE";
    $this->changeStatusToCorrect();
    return $this->status;
  }

  private function changeStatusToCorrect()
  {
    $this->status = "INACTIVE";
    // Xử lý sử dụng  $status}
    // $status を使う処理  }
}

特に問題がないのであれば、$status をローカル変数にすることで、各変数のスコープが狭くなります。

Nếu không có vấn đề gì, việc dùng $status làm một biến cục bộ sẽ làm thu hẹp phạm vi của các biến.

✅ Good Code

<?php
class StatusLogic
{
  private function updateStatus()
  {
    $status = "ACTIVE";
    $status = $this->changeStatusToCorrect($status);
    return $status;
  }

  private function changeStatusToCorrect(string $status)
  {
    $status = "INACTIVE";
    // Xử lý sử dụng  $status
    // $status を使う処理
    return $status;
  }
}


アクセス修飾子を変えていい? / Tôi có thể thay đổi access modifier không?

#アクセス修飾子 #プロパティ #スコープ

以下のクラスでは、メンバ変数である $userName には private アクセス修飾子が指定されています。

Ở class sau, private access modifier được chỉ định cho biến member $userName.

<?php
class User
{
  /** @var int User id */
  private int $userId;

  /** @var string User Name */
  private string $userName;    // ★★★★★

  public function __construct(int $userId, string $userName)
  {
    $this->userId = $userId;
    $this->userName = $userName;
  }
}

では、あなたが上司から、「このUserクラスのインスタンスから、ユーザ名 を取得して表示するように」と指示された場合、どのように修正しますか??

この場合、以下のようにアクセス修飾子を変更することは避けた方がいいでしょう。

Vì vậy, nếu sếp của bạn yêu cầu bạn "lấy tên user từ instance của class User này để hiển thị", bạn sẽ chỉnh sửa như thế nào ??

Trong trường hợp này, bạn nên tránh thay đổi access modifier như sau.

❌ Bad Code

<?php
{
  /** @var int User id */
  private int $userId;

  /** @var string User Name */
  public string $userName;   // ❌  Thay đổi từ `private` thành `public`!!

  public function __construct(int $userId, string $userName)
  {
    $this->userId = $userId;
    $this->userName = $userName;
  }
}

// Xử lý khác
// 他の処理
$user = new User(1, "Jonh");
$userName = $user->userName;    // Vì đã đặt `public` nên có thể đọc được. 
print($userName);

アクセス修飾子をprivate から public に変更すると、このクラスのインスタンスを使う全ての処理から変数の参照と変更ができてしまうため、変数のスコープがとても広くなってしまいます。

最初にこのUserクラスを実装した人は、何かの理由があって $userNameprivate にしていたはずです。
安易にアクセス修飾子を変更せず、変数のスコープを広くしない修正方針を考えるべきです。

Nếu bạn thay đổi access modifier từ private thànhpublic, thì phạm vi biến sẽ trở nên rất rộng vì có thể tham chiếu và thay đổi các biến từ tất cả các xử lý sử dụng các instance của class này.

Người đã implement class user này lúc đầu chắc có lẽ đã đặt $userName thành private vì có lý do nào đó. Bạn nên suy nghĩ về phương châm fix mà không thay đổi access modifier và không mở rộng phạm vi của các biến.

たとえば、以下のように getter() を作成することで、少なくとも他の処理から $userName の上書きは行われなくなります。

Ví dụ: bằng cách tạo getter() như sau, ít nhất xử lý khác sẽ không ghi đè lên $userName.

✅ Good Code

<?php
class User
{
  private int $userId;
  private string $userName;

  public function __construct(int $userId, string $userName)
  {
    $this->userId = $userId;
    $this->userName = $userName;
  }
  
  public function getUserName()
  {
    return $this->userName;
  }
}

  :
  :

// Xử lý khác
// 他の処理
$user = new User(1, "Jonh");
$userName = $user->getUserName();
print($userName);

とはいえ、場合によっては private な値を他の処理からは全く読み取って欲しくないコードもあります。
そのときは、その値を使う処理をクラス内に実装するなどの工夫が必要です。

どのような意図でそのコードが記載されているかを読み取って、適切な実装を考えることが大切です。

Tuy nhiên, tùy trường hợp, cũng có code mà bạn không muốn đọc được tất cả các giá trị private từ xử lý khác. Trong trường hợp đó, cần phải công phu hơn chẳng hạn như implement xử lý sử dụng giá trị đó ở trong class.

Điều quan trọng là phải đọc được code đó đang được mô tả với ý định như thế nào và suy nghĩ về cách implement phù hợp.

1-4. Specify the Type

#型

  • 型を指定する
  • Chỉ định type

さて、せっかくラクスのブログでPHPで記事を書いているので、PHPならではの注意事項も記載しておきます。

PHPはバージョンアップするたびに、型に厳格になってきました。文字列型と数値型の足し算ができなくなったり、count() 関数の引数に配列以外の値を指定するとエラーになるようになった仕様変更がこれにあたります。
これに伴って、変数に予期しない型の値が入っていると、思わぬエラーが生じる場合があります。

そのため、今後作成するPHPコードでは、各変数にどの型が入るかを意識し、違う型の値が入らないようにするべきでしょう。

Nhân tiện, tuy không có mô tả trong Readable code nhưng vì tôi đã viết một bài blog về PHP trên blog của Rakus, nên tôi sẽ mô tả thêm mục chú ý chỉ đối với PHP.

Mỗi khi PHP được nâng cấp, nó trở nên khắt khe hơn về type. Đó là thay đổi specification như: không thể cộng string type với numeric type, hoặc xảy ra lỗi khi chỉ định một giá trị không phải là mảng làm đối số của hàm count(). Cùng với điều này, nếu thêm kiểu giá trị không mong muốn vào biến, thì sẽ có trường hợp phát sinh error không lường trước được.

Do đó, trong code PHP mà bạn sẽ viết trong tương lai, bạn nên ý thức ở các biến sẽ chứa type nào và tránh đưa vào các giá trị có type khác .

たとえば、以下のように $result に数値や文字列が入るようなコードは避けましょう。

Ví dụ: chúng ta nên tránh code có chứa numerical value hoặc character string ở $result như bên dưới.

❌ Bad Code

<?php
$countRows = count($rows);

if($countRows > 0) {

  $result = $countRows;
  
} else {

  $result = "ERROR: NO RESULT";

}

✅ Good Code

<?php
$countRows = count($rows);

if($countRows > 0) {

  $result = $countRows;
  
} else {

  $result = 0;

}

2. 条件式に関するテクニック /
2. Các Technique liên quan đến biểu thức điều kiện

コード中に条件式があると考えることが増えるため読みにくくなってしまいます。
とはいえ、コードから条件式をなくすことはできませんので、できるだけわかりやすい記述をすることが大事です。

Nếu có các biểu thức điều kiện trong code thì nó khó đọc hơn vì bạn phải suy nghĩ nhiều. Tuy nhiên, biểu thức điều kiện không thể bị xóa khỏi code, vì vậy điều quan trọng là phải viết nó dễ hiểu nhất có thể.

2-1. The Order of if/else Blocks

#if文 #値の比較

  • if/else 文の順番
  • Thứ tự của lệnh if/else

特に理由がある場合以外は、否定より肯定の条件を先に書きましょう。
否定条件が先にあると、否定条件が重要な処理に見えてしまいます。

(もちろん、否定条件の方が重要な処理であれば、否定条件をはじめに書いても問題ありません。)

Trừ khi có lý do đặc biệt, còn lại hãy viết điều kiện khẳng định trước điều kiện phủ định. Nếu điều kiện phủ định có ở phía trước, thì sẽ cho rằng điều kiện phủ định là một xử lý quan trọng.

(Tất nhiên, nếu điều kiện phủ định là 1 xử lý quan trọng, bạn có thể viết điều kiện phủ định ngay từ đầu mà không có bất kỳ vấn đề nào.)

❌ Bad Code

<?php
if($aaa !== $bbb) {
  ...
} else {
  ...
}

✅ Good Code

<?php
if($aaa === $bbb) {
  ...
} else {
  ...
}

2-2. Strict Comparisons

#型 #PHP #値の比較

  • 厳密な比較
  • So sánh chặt chẽ

こちらはPHPならではの注意事項です。

Đây là một số lưu ý chỉ có ở PHP. Tuy nó không được đề cập trong Readable code, nhưng tôi sẽ đề cập đến nó vì nó là một vấn đề lớn.

先ほども述べた通り、PHPはバージョンアップするたびに、型に厳格になってきたため、変数に予期しない型の値が入っていると、思わぬエラーが生じる場合があります。

そのため、条件式でも厳格に型を指定した比較を使い、事故を減らすべきです。

Như đã đề cập trước đó, PHP đã trở nên chặt chẽ hơn về type mỗi khi nó được nâng cấp version, vì vậy nếu một biến chứa kiểu giá trị không mong đợi thì sẽ có trường hợp phát sinh error không lường trước được.

Do đó, bạn cũng nên giảm thiểu các sự cố bằng cách sử dụng các phép so sánh đã được chỉ định type một cách chặt chẽ ngay cả ở biểu thức điều kiện.

以下のように、できるだけ厳格な比較を行いましょう。

Thực hiện phép so sánh nghiêm ngặt nhất có thể như bên dưới.

❌ Bad Code

<?php
if($value == 0) {...}
if($value != 0) {...}

✅ Good Code

<?php
if($value === 0) {...}
if($value !== 0) {...}

2-3. Not Use Conditional Branch

#if文

  • 条件分岐を使わない
  • Không sử dụng phân nhánh điều kiện.

さて、ここまで条件式についてのテクニックを述べてきましたが、そもそも条件分岐は少ない方が読みやすいコードになります
条件分岐はロジックを記述する上で必要不可欠ではありますが、あちこちに if 文が存在するコードはとても読みづらくなってしまいます。

Đến đây tuy tôi đã mô tả Technique về các biểu thức điều kiện, nhưng vốn dĩ phân nhánh điều kiện mà có ít nhánh thì càng dễ đọc. Mặc dù phân nhánh điều kiện cần thiết khi mô tả logic, nhưng code mà có tồn tại câu lệnh if ở chỗ này chỗ kia thì sẽ trở nên rất khó đọc. Do đó, mặc dù nó không có đề cập ở Readable code nhưng tôi sẽ đề cập đến việc giảm câu lệnh if.

条件分岐を減らすテクニックはいくつかありますが、ここでは1つだけ紹介します。

以下の例では、ユーザが画面から選択したチャットツールに、メッセージを投稿します。
ユーザは、"LINE"、"Slack"、"Zalo" のうち、いずれかのチャットへメッセージを投稿することができます。

Có một số Technique để giảm phân nhánh điều kiện, nhưng tôi chỉ giới thiệu ở đây một Technique.

Ở ví dụ dưới đây,sẽ post message vào Chat tool mà user lựa chọn từ màn hình. User có thể post message lên bất kỳ Chat nào trong "LINE", "Slack" hoặc "Zalo".

❌ Bad Code

<?php

$chatType = $request->get("chatType");
$postMessage = $request->get("postMessage");

switch($chatType) {
  case CHAT_TYPE_LINE:
    $lineAccount = getLineAccount();
    $linePostResult = postMesseageToLine($postMessage);
    break;

  case CHAT_TYPE_SLACK:
    $slackAccount = getSlackAccount();
    $slackPostResult = postMesseageToSlack($postMessage);
    break;

  case CHAT_TYPE_ZALO:
    $zaloAccount = getZALOAccount();
    $zaroPostResult = postMesseageToZalo($postMessage);
    break;
}

  :
  :
  :

// Phần hiển thị màn hình
// 画面表示部分
switch($chatType) {
  case CHAT_TYPE_LINE:
    if($linePostResult === "success") {
      displayMessage("Posted a message to LINE.");
    } else {
      displayMessage("Error occurred when post a message to LINE.");
    }
    break;

  case CHAT_TYPE_SLACK:
    if($linePostResult === "success") {
      displayMessage("Posted a message to Slack.");
    } else {
      displayMessage("Error occurred when post a message to Slack.");
    }
    break;

  case CHAT_TYPE_ZALO:
    if($linePostResult === "success") {
      displayMessage("Posted a message to Zalo.");
    } else {
      displayMessage("Error occurred when post a message to Zalo.");
    }
    break;

}

上記のように、各チャットの処理をswitch文(もしくはif分)で記載すると、処理が煩雑になってしまいます。
そのため、以下のように、各チャットの処理を記載したクラスのインスタンスを用意することで、条件分岐を減らすことができます。
また、条件分岐内の処理も少なくすることができます。

Như đã đề cập ở trên, nếu mô tả xử lý các Chat bằng câu lệnh switch (hoặc if), thì xử lý sẽ trở nên phức tạp. Do đó, phân nhánh có điều kiện có thể được giảm bớt bằng cách chuẩn bị instance class có mô tả xử lý của mỗi Chat như bên dưới. Ngoài ra, cũng có thể giảm bớt xử lý trong phân nhánh điều kiện.

✅ Good Code

<?php
$chatType = $request->get("chatType");
$postMessage = $request->get("postMessage");

switch($chatType) {
  case CHAT_TYPE_LINE:
    $chat = new Line();
    break;

  case CHAT_TYPE_SLACK:
    $chat = new Slack();
    break;

  case CHAT_TYPE_ZALO:
    $chat = new Zalo();
    break;

}

// Ở các class chat
// có một method `post ()` để post message vào từng Chat
// 各チャットのクラスには、
// それぞれのチャットへメッセージを投稿する `post()` メソッドがある
$postResult = $chat->post($postMessage);
  :
  :
  :
// Phần hiển thị trên màn hình
// 画面表示部分
if($postResult === "success"){

  displayMessage("Posted a message to " . $chat->name());

} else {

  displayMessage("Error occurred when post a message to " . $chat->name());

}

3. ロジックの簡略化を行うテクニック / Các kỹ thuật thực hiện đơn giản hóa logic

難しいロジックを読み解く場合、脳にはそれだけ負荷がかかってしまいます。
できるだけロジックは簡単にし、誰でも読み解けるようにすることが重要です。

Khi đọc hiểu những logic khó, nó sẽ khiến não bạn căng thẳng. Việc đơn giản hóa logic trong khả năng có thể, để ai đọc vào cũng có thể hiểu được là điều quan trọng.

3-1. Early Return

#EarlyReturn #ガード節 #早期リターン

  • 早くに return する
  • Early Return

関数にて早くに処理を終了できる場合は、終了できる状態になったときに値を return しましょう。
もう他に処理を行う必要がないのに、開発者は return を見つけるまでその処理を読まなければいけなくなってしまいます。

Trường hợp có thể kết thúc xử lý sớm ở hàm, thì hãy return giá trị khi nó đã ở trạng thái có thể kết thúc. Mặc dù không cần thực hiện thêm các xử lý khác nhưng Developer phải đọc các xử lý đó đến khi tìm thấy return.

❌ Bad Code

<?php

function isValidInputedUser($inputedUser):bool
{
  $result = true;
  
  if($inputedUser->id <= 0) {

    $result = false;

  } else {
   ...............
   ...............
   ...............

      :
      :

   ...............
   ...............
   ...............
  }
  
  return $result;
}

以下のように記載すれば、最後まで読まなくても、id が0以下の場合は他になにもせず処理が終わることがすぐにわかります。

Nếu mô tả như sau, thì dù không đọc đến cuối, bạn có thể thấy ng ay rằng nếu id nhỏ hơn hoặc bằng 0, thì xử lý kết thúc mà không cần làm gì khác.

✅ Good Code

<?php

function isValidInputedUser($inputedUser):bool
{

  if($inputedUser->id <= 0) {

    return false;

  }
 ...............
 ...............
 ...............

    :
    :

 ...............
 ...............
 ...............
  
  return $result;
}

また、このテクニックは、関数の内部だけでなく、ループ内でも有効です。
ループ内で処理を終了できる場合は、break を利用しましょう。

Technique này không chỉ hữu ích ở bên trong hàm, mà còn ở bên trong loop. Trường hợp có thể kết thúc xử lý ở bên trong loop, thì hãy sử dụng break.

❌ Bad Code

<?php

// Lấy 1 recipe bạn chưa từng tạo
// 作ったことがないレシピを1件取得する
$done = false;
foreach ($recipeList as $recipe) {

  if(!$done) {

    if (!$recipe->isMade) {
      $done = true;
      $suggestRecipe = $recipe;
    }

  }

}

return render("sugestOneRecipe.html", $suggestRecipe);

✅ Good Code

<?php

// Lấy 1 recipe bạn chưa từng tạo
// 作ったことがないレシピを1件取得する
foreach ($recipeList as $recipe) {

  if (!$recipe->isMade) {
    $suggestRecipe = $recipe;
    break;      // Early Return!!
  }

}

return render("sugestOneRecipe.html", $suggestRecipe);

3-2. Minimize Nesting

#ネスト #EarlyReturn #ガード節

  • ネストを浅くする
  • Làm cho nest cạn đi

以下のようなコードは、とても読みにくく感じないでしょうか。

Bạn có cảm thấy code như bên dưới rất khó đọc không?

❌ Bad Code

<?php

if($val < 1) {
  if($val < 0.5) {
    if($val < 0.05) {
      if($val < 0.005) {
        if($val < 0.0005) {
          :
          :

深いネストがあると、考えることが多くなるため読みにくいコードになります。できるだけネストは浅くしましょう。

個人差はあると思いますが、私は Level 3 のネストがあると解消できないかを考え、Level 4 になるとなんとしても避けようとします。

Việc có nest sâu sẽ làm cho code khó đọc vì nó khiến bạn phải suy nghĩ rất nhiều. Hãy làm cho nest cạn nhất có thể.

Tôi nghĩ rằng có những sự khác nhau với mỗi người, nhưng nếu có nest ở level 3, thì tôi suy nghĩ xem có thể loại bỏ nó được không và nếu là level 4 thì tôi sẽ cố gắng tránh nó bằng mọi cách.

ネストを浅くするためには、先ほど紹介した Early Return が有効です。 以下のようにif文の条件を整理することで解消できます。

Để làm cho nest cạn thì phải ON Early Return đã tôi đã giới thiệu trên đây. Có thể xóa bằng việc điều chỉnh điều kiện của câu lệnh if như dưới đây.

❌ Bad Code

<?php

$user = loadUser($userId);

if($user->loadResult === "success") {

  if(!$user->hasPermission) {
    $errorMessage = "ERROR: Permission.";
  } else {
    $errorMessage = "";
  }

} else {

  $errorMessage = "ERROR: Failed load.";

}

$display["user"] = $user;
$display["errorMessage"] = $errorMessage;
return render("userInfo.html", $display);

以下の例では、Early Return を利用することで、上記コードのネストを1 Level 減らすことができました。

Trong ví dụ dưới đây, chúng tôi có thể giảm bớt nest của code viết trên xuống 1 level bằng cách sử dụng Early Return.

✅ Good Code

<?php
$user = loadUser($userId);

$display["user"] = $user;

if($user->loadResult !== "success") {
  $display["errorMessage"] = "ERROR: Failed load.";
  return render("userInfo.html", $display);
}

if(!$user->hasPermission) {
  $display["errorMessage"] = "ERROR: Permission.";
  return render("userInfo.html", $display);
}

$display["errorMessage"] = "";
return render("userInfo.html", $display);

4. 目的に合わせたコードにするテクニック /
Techniques làm cho code đúng bản chất

基本的に、プログラムで行う処理は小さな問題に分割されている方が便利です。

例えば、大きな関数があり、その関数内で全ての処理を行っているとしたら、 なにか仕様変更があったときにはその関数内で行われている全ての処理をテストする必要があります。

また、大きな関数では、コードを読むときもこの関数では最終的に何がやりたいのか分かりづらくなっているでしょう。

そのため、保守性が高く読みやすいコードを書くためには、その処理でやりたい本当の目的を明確にして、 目的でない問題は他の処理に切り出すことが必要です。

Về cơ bản, rất tiện lợi khi chia xử lý thực hiện trong program thành các vấn đề nhỏ. Ví dụ: nếu có một hàm lớn và đã thực hiện tất cả xử lý trong hàm đó, khi có sự thay đổi specification gì đó, thì cần phải test tất cả các xử lý được thực hiện trong hàm đó. Ngoài ra, đối với hàm lớn, cả khi đọc code, bạn cũng bị khó biết được là cuối cùng bạn muốn làm gì trong hàm này đúng không. Do đó, để viết code có tính bảo trì cao và dễ đọc, hãy làm rõ vấn đề bản chất mà bạn muốn làm trong xử lý này, cần cắt các vấn đề không phải là bản chất sang xử lý khác.

4-1. Be General-Purpose Code

  • 汎用コードにする
  • Chọn code đa dụng

#疎結合 #密結合 #独立したコード #責務 #外出し

処理を他の関数に切り出すとき、
できればその関数はアプリケーションに無関係な独立したコードになっている方がいいでしょう。
もし、そのコードがアプリケーションの仕様に依存していた場合は、
コードの内容を理解するためにアプリケーションの仕様を理解しておく必要がありますし、
アプリケーションの仕様が変更されたとき、そのコードも合わせて修正する必要があります。

また、独立したコードは、そのコードのみで実行することができるため、 テストも簡単に行うことができます。

切り出したコード自体はできるだけ他の処理と依存せず、疎結合な状態を保つことで、 保守性の高いコードになります。

Khi cắt xử lý sang hàm khác, nếu có thể thì hàm đó nên là code độc lập không liên quan đến application đúng không. Nếu code đó đã tồn tại trong specification của application, thì cần phải hiểu spec của application để hiểu nội dung code, và khi spec của application bị thay đổi, thì code đó cũng cần chỉnh sửa cho phù hợp.

Ngoài ra, code độc lập có thể thực thi chỉ với code đó, nên việc test cũng có thể thực hiện dễ dàng.

Chính bản thân code đã cắt ra không phụ thuộc với xử lý khác hết mức có thể, và giữ trạng thái loosely coupling, nên sẽ trở thành code có tính bảo trì cao.

以下はアプリケーションへの依存性が高いコードです。

Dưới đây là code có tính phụ thuộc vào application cao.

❌ Bad Code

<?php
// Hiển thị recipe yêu thích của user
// ユーザのお気に入りレシピを表示する

$favoriteRecipeName = getUserfavoriteRecipe();

print "<p>{$favoriteRecipeName}</p>";


/**
 * Lấy recipe yêu thích của user
 * ユーザのお気に入りレシピを取得する
 * @return string Recipe name yêu thích / お気に入りレシピ名
 */
function getUserfavoriteRecipe():string
{
  $userId = $_COOKIE["userId"];  // ★★★★★
  $user = new User($userId);
  return $user->favoritRecipe->name;
}

getUserfavoriteRecipe() の中に注目してください。
関数の中で cookie からユーザIDを取得しています。
しかし、これはアプリケーションのどこかでcookieにユーザIDを保存しているため実行できるのであって、
もし、cookieにユーザIDが保存されていない場合はエラーになってしまいます。

つまり、「ユーザIDがcookieに保存されている」というアプリケーションの仕様に依存してしまっている状態です。

Hãy tập trung vào trong getUserfavoriteRecipe() . Đã lấy user ID từ cookie trong hàm. Tuy nhiên, điều này có thể thực thi vì đã lưu user ID vào cookie ở đâu đó trong application, nên nếu user ID không được lưu vào cookie thì sẽ bị lỗi

Nói cách khác, là trạng thái phụ thuộc vào spec của application gọi là  "user ID được lưu trong cookie".

処理を外出しする場合は、以下のように仕様から独立したつくりにするべきでしょう。

Khi going out xử lý, thì nên tạo độc lập từ spec như dưới đây nhỉ.

✅ Good Code

<?php
// Hiển thị recipe yêu thích của user
// ユーザのお気に入りレシピを表示する

$userId = $_COOKIE["userId"];    // ★★★★★
$favoriteRecipeName = getUserfavoriteRecipe($userId);

print "<p>{$favoriteRecipeName}</p>";


/**
 * Lấy recipe yêu thích của user
 * ユーザのお気に入りレシピを取得する
 * @param int $userId
 * @return string Recipe name yêu thích / お気に入りレシピ名
 */
function getUserfavoriteRecipe(int $userId):string
{
  $user = new User($userId);
  return $user->favoritRecipe->name;
}

ユーザIDを関数の外で取得して引数で渡すことにより、
getUserfavoriteRecipe() は仕様から解放されました。
これで関数単体のテストも容易に作成することができます。

Do lấy user ID ở ngoài hàm và truyền với đối số, nên getUserfavoriteRecipe() đã được giải phóng khỏi specification. Điều này làm cho có thể tạo unit test cho hàm dễ dàng.

ただし、もちろんやりすぎはよくありません。
あまりに処理を分けすぎると、処理があちらこちらに飛んでしまい、処理の流れを追いづらくなります。
適切な粒度を判断して、処理を分けるようにしましょう。

Nhưng tất nhiên, làm quá mức là không tốt. Nếu chia xử lý quá nhiều, thì xử lý sẽ bay sang chỗ này chỗ kia, dẫn đến khó theo dõi luồng xử lý. Hãy phán đoán độ chi tiết phù hợp để phân chia xử lý.

4-2. Simple is more important than Easy

  • Easy より Simple が大事
  • Simple quan trọng hơn Easy

#SimpleMadeEasy #Simple #責務

さて、この記事は日本語とベトナム語で書かれていますが、ここで少し英語の話をさせてください。
(翻訳チームのみなさん、ややこしくてすみません。。。)

Nhưng tất nhiên, làm quá mức là không tốt. Nếu chia xử lý quá nhiều, thì xử lý sẽ bay sang chỗ này chỗ kia, dẫn đến khó theo dõi luồng xử lý. Hãy phán đoán độ chi tiết phù hợp để phân chia xử lý.

"Easy" と "Simple" の違いを考えたことはあるでしょうか?

日本語では両方とも「簡単」と訳せます。(ベトナム語はどうでしょう?)
しかし、英語でのこれらには微妙なニュアンスの違いがあります。

Bạn đã từng suy nghĩ về sự khác nhau giữa "Easy" và "Simple" đúng không? Trong tiếng Nhật thì cả 2 đều dịch là「Đơn giản」.(Tiếng Việt thì sao?) Tuy nhiên, có sự khác biệt nhỏ về sắc thái giữa các từ này trong tiếng Anh.

このニュアンスの違いについて、Clojure の作者である Rich Hickey が数年前に "Simple Made Easy" という発表をしています。*2*3

Về sự khác biệt sắc thái này, Rich Hickey là tác giả của Clojure đã có phát biểu gọi là "Simple Made Easy" trong vài năm trước. *4*5

この発表によると、"Simple" の語源はラテン語の "simplex" であり、「1回折る」「1回編む」などの意味をもち、いずれも「単一の」という意味を含みます。
また、対義語は 「絡まった」「もつれている」といった意味を持つ "Complex" です。

Theo phát biểu này, nguồn gốc của từ "Simple" là "simplex" từ tiếng Latinh, có nghĩa là "gấp 1 lần", "đan 1 lần", v.v., tất cả đều bao hàm ý nghĩa là " của đơn nhất". Ngoài ra, từ trái nghĩa là "Complex", có nghĩa là "vướng" hoặc "rối".

一方、"Easy" の語源はラテン語の "adjacens" と言われており、「近くにある」「身近な」「親しみのある」という意味をもち、
対義語は "Difficult" か "Hard" です。

Mặt khác, nguồn gốc của từ "Easy" được gọi là "adjacens" từ tiếng Latinh, có nghĩa là「Ở gần」「Gần gũi」「Thân thiện」, Từ trái nghĩa là "Difficult" hoặc "Hard".

ここで大事なことは、"Easy" の「身近な / 親しみのある」という意味は、あくまで主観的、相対的な意味であり、
"Simple" の「単一の」という意味は客観的であるという点です。

Điều quan trọng ở đây là ý nghĩa "Gần gũi / Thân thiện" của từ "Easy" chỉ mang tính chủ quan và tương đối. Ý nghĩa của " của đơn nhất" trong "Simple" là điểm mang tính khách quan.

たとえば、メールを送信する関数があるとします。

この関数はとても高機能で、引数に From アドレスや、To アドレスを指定できるだけでなく、
添付ファイルがある場合は添付ファイルのパスを引数に指定することで利用できるほか、
To アドレスを配列にすれば複数の To アドレスを指定でき、
さらに、テキストと HTML の本文文字列をそれぞれ引数に渡せばマルチパートメールにも送信できます。

Ví dụ: giả sử có một hàm gửi mail. Hàm này có tính năng rất cao, không chỉ có thể chỉ định From address và To address cho đối số mà còn nếu có file đính kèm, bạn còn có thể sử dụng khi chỉ định path của file đính kèm cho đối số, ngoài ra nếu đặt To address là mảng, thì còn có thể chỉ định nhiều To address, thêm nữa, nếu truyền chuỗi body của text và HTML lần lượt cho đối số thì còn có thể gửi tới multipart mail.

つまりこの関数は、あらゆるメールを "Easyに" 作成できる関数です。

Nói cách khác, hàm này là hàm có thể tạo tất cả các mail một cách "Easy"

しかし、引数によってさまざまな条件があるため、この関数の中には非常に多くの if 文が含まれます。
(添付ファイルがあった場合の if 文、マルチパートメールを送る場合の if 文 etc...)

Tuy nhiên, bao gồm rất nhiều câu if trong hàm này vì có các điều kiện khác nhau tùy thuộc vào đối số. (Câu if khi có file đính kèm, câu if khi gửi multipart mail, v.v.)

そのため、この関数は "Complexである" とも言え、
下図の②にあたります。

Vì vậy, hàm này có thể gọi là "Complex", tương ứng với ② trong hình dưới đây.

さて、この関数が存在するシステムで「SMTPサーバを指定する」機能を実装することになったとします。
この場合、上記の "Easyな" 関数を修正することは非常に難しくなります。

なぜなら、条件分岐がたくさんあるため考慮事項が増えてしまい、テストも非常に難しくなってしまいます。
また、この関数を作成した本人は内容を理解できるかもしれませんが、
他の人が見ると条件分岐がとても多く読みづらいコードに見えてしまうことでしょう。

Bây giờ, giả sử quyết định implement chức năng "chỉ định SMTP server" trên system tồn tại hàm này. Trong trường hợp này, sẽ rất khó chỉnh sửa hàm có tính "Easy" viết trên.

Lý do là vì có nhiều phân nhánh điều kiện nên mục cân nhắc tăng lên và việc test cũng trở nên rất khó khăn. Ngoài ra, chính người tạo ra hàm này có lẽ có thể hiểu nội dung, nhưng mà nếu người khác xem, thì sẽ thấy rất nhiều nhánh điều kiện và có vẻ như là code khó đọc.

この関数は、とある1場面ではとても便利で Easy かもしれませが、
Complex になっているため、拡張性がなく他の場面では Easy でなくなってしまいます。

Hàm này có thể rất tiện lợi và Easy trong 1 tình huống nào đó, nhưng mà vì nó là Complex, nên với tình huống khác không có tính mở rộng thì sẽ trở nên không Easy.

この場合、Easy よりも、Simple であることを重視すべきです。

Trường hợp này, thì nên chú trọng là Simple hơn là Easy.

Simple、つまり単純でわかりやすいことを心がけた場合、そのコードは拡張性に優れ、さらに他者にも読みやすくなります。
もちろん、Easy であることはいいことですが、Easy であることを求めたため、Complex になってしまっては意味がありません。
拡張性がなく、バグを生み出しやすいコードより、利用するために手間がかかるが単純でわかりやすいコードの方がずっと嬉しいです。

実装を行うときは、Easy より Simple を重視すべきです。

Simple, tức là nếu giữ suy nghĩ cố gắng để cho nó đơn giản và dễ hiểu, thì code đó sẽ có tính mở rộng, và cả người khác cũng dễ đọc hơn nữa. Tất nhiên, Easy là tốt, nhưng mà vì đã yêu cầu là Easy, nên nếu trở thành Complex thì không có ý nghĩa gì. So với code không có tính mở rộng, dễ sinh ra bug, thì dù mất thời gian công sức để sử dụng tôi vẫn thích code đơn giản dễ hiểu hơn.

Khi thực hiện implement, nên chú trọng Simple hơn là Easy.

4-3. Avoid Too Fat Code

  • 肥大化したコードを避ける
  • Tránh code cồng kềnh

#やりすぎ #仕様を減らす

コードは少なく短い方が保守性や可読性が高くなります。
これの解決方法は、不要な仕様を減らすことだけではありません。

そもそものコードを小さく関数やクラスに分割し、
他のコードから分離することで、事実上コードを短くすることができます。

他の処理から分離されていることが明確なコードは、
仕様の追加時やバグの修正時にも編集箇所が局所的になるため、修正や品質の担保が容易になります。

他の項目でも触れましたが、汎用的なコードを意識することや、処理を分離することなどを考えつつ、
コードの "重量" を意識し、できるだけ軽量なコードを書くよう心がけましょう!

Simple, tức là nếu giữ suy nghĩ cố gắng để cho nó đơn giản và dễ hiểu, thì code đó sẽ có tính mở rộng, và cả người khác cũng dễ đọc hơn nữa. Tất nhiên, Easy là tốt, nhưng mà vì đã yêu cầu là Easy, nên nếu trở thành Complex thì không có ý nghĩa gì. So với code không có tính mở rộng, dễ sinh ra bug, thì dù mất thời gian công sức để sử dụng tôi vẫn thích code đơn giản dễ hiểu hơn. Khi thực hiện implement, nên chú trọng Simple hơn là Easy.

おわりに /
Lời kết

先の記事でも触れましたが、日本とベトナムでは文化だけでなく、エンジニア向けのコンテンツ量も違います。
お互いに言語も違いますし、英語を使ったとしてもお互いの母国語ではないためどこまで意図が伝わるかは不確定です。

この記事を利用することで、ベトナムとの齟齬のない開発が行えることを願います。

Như tôi đã đề cập trong bài viết trước, không chỉ văn hóa mà lượng nội dung dành cho kỹ sư cũng khác nhau giữa Japan và Việt Nam. Ngôn ngữ khác nhau, và ngay cả khi bạn sử dụng tiếng Anh, cũng không chắc chắn ý đồ sẽ được truyền đạt vì nó không phải là ngôn ngữ mẹ đẻ của cả 2.

Chúng tôi hy vọng rằng khi sử dụng bài viết này, sẽ có thể phát triển mà không có sự hiểu lầm với Việt Nam.


  • RAKUS Vietnam Co.,Ltd
    Rakus Việt Nam đang tích cực tuyển dụng kỹ sư (Java, PHP) và member QA. Bạn có muốn cùng tạo ra những sản phẩm SaaS với chất lượng như blog này không? Nếu bạn quan tâm, hãy ứng tuyển nhé.
    ラクベトナムでは、エンジニア(Java, PHP)やQAメンバの採用を積極的に行っています。 このブログのような品質を意識した自社SaaSプロダクトを一緒に作りませんか? ご興味ありましたら、是非応募をお願いします。 www.rakus.com.vn

  • エンジニア中途採用サイト
    ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
    ご興味ありましたら是非ご確認をお願いします。
    20210916153018
    https://career-recruit.rakus.co.jp/career_engineer/

  • カジュアル面談お申込みフォーム
    どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
    以下フォームよりお申込みください。
    rakus.hubspotpagebuilder.com

  • イベント情報
    会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!

◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.