くらしのマーケット開発ブログ

「くらしのマーケット」を運営する、みんなのマーケット株式会社のテックブログ。積極採用中です。

設計の原則の紹介とくらしのマーケットのシステム

f:id:curama-tech:20181220111408p:plain

自称webエンジニアの高橋です。ソフトウェアを設計するにあたってよく知られている原則に、SOLID原則というものがあります。これは、

  • 単一責任の原則(Single Responsibility Principle)
  • オープン・クローズドの原則(Open-Closed Principle)
  • リスコフの置換原則(Liskov Substitution Principle)
  • インターフェース分離の原則(Interface Segregation Principle)
  • 依存関係逆転の原則(Dependency Inversion Principle)

の5つの原則の頭文字を並べたものです。

本記事では、くらしのマーケットのプロダクションコードの一部を例/反例として、上の原則の紹介をしてみます。なお、以下に紹介するコードは全てTypeScriptです。

単一責任の原則

単一責任の原則はモジュール(クラス)を変更する理由は1つ以上存在してはならないという原則です。 例えば以下のようなコードがAngularのコンポーネントとして定義されているとします。

@Component({
    selector: "terry-ticket-list",
    templateUrl: "./terryTicketList.html",
    styleUrls: ["./terryTicketList.scss"]
})
export class TerryTicketListComponent implements OnInit {
    // 省略

    private getTerryTickets(
        params: GetTerryTicketListParameters,
    ): void {
      // 100行に渡るajax処理
    }
}

Angularのコンポーネントはviewに対する最小限のロジックのみを書くべきですが、このクラスではデータ取得処理を長々とコンポーネントに記述してしまっています。 例えばこの状態で、データ取得処理のモジュールを入れ替えたいという要件が発生した場合(例えば@angular/http@angular/common/httpに変更したいときなど!)、 viewには直接関係ない理由でこのコンポーネントを修正する必要が出てきます。

この場合は以下のように ticketApiService 等の別のモジュールを作成して、処理を委譲してしまうことで解決できそうです。

@Component({
    selector: "terry-ticket-list",
    templateUrl: "./terryTicketList.html",
    styleUrls: ["./terryTicketList.scss"]
})
export class TerryTicketListComponent implements OnInit {
    constructor(
        private ticketApiService: TicketApiService,
    ) {
    }

    // 省略
    private getTerryTickets(
        params: GetTerryTicketListParameters
    ): void {
      return this.ticketApiService(params);
    }
}

これで、viewに関するロジックとデータ取得のロジックが分離されました。

オープン・クローズドの原則

オープン・クローズドの原則は、モジュール(クラス)は拡張に対して開いていなければならず、修正に対して閉じていなければならないという原則です。 ところで、拡張に対して開いているとはどのような状態でしょうか?

export class TerryTicketDetailComponent implements OnInit {
    @ViewChild("placeholder", { read: ViewContainerRef })
    public placeholder: ViewContainerRef;
    private componentRef: ComponentRef<TerryCardComponent>;

   // 省略
    private initChildComponent(): void {
        let currentComponent: typeof TerryCardComponent;
        switch (this.terryTicket.ticket.terryTicketTypeId) {
            case TerryTicketTypes.AlertPostBadReview:
                currentComponent = CardPostBadReviewComponent;
                break;
                // 省略
        }
        const componentFactory = this.compiler.resolveComponentFactory<TerryCardComponent>(currentComponent);
        this.componentRef = this.placeholder.createComponent(componentFactory);
    }
}

例えば上のコンポーネントでは、initChildComponent関数の中で子コンポーネントをswitch文で動的に設定しています。ここで、子コンポーネントをさらに追加したい場合は、case文を新たに追加して修正する必要があります。 このままでもストラテジーパターン的な実装ではありますが、さらに責務を分離するなら、以下のような関数を定義すると良いでしょう。

export class TerryTicketDetailComponent implements OnInit {
    @ViewChild("placeholder", { read: ViewContainerRef })
    public placeholder: ViewContainerRef;
    private componentRef: ComponentRef<TerryCardComponent>;

   // 省略
    private initChildComponent(): void {
        const currentComponent = this.dispatch(terryTicketTypeId)
        const componentFactory = this.compiler.resolveComponentFactory<TerryCardComponent>(currentComponent);
        this.componentRef = this.placeholder.createComponent(componentFactory);
    }

    private dispatch(ticketTypeId: TerryTicketTypeIds): TerryCardComponent {
        const components = this.getComponents();
        for (const component of components) {
          if (ticketTypeId === component.ticketTypeId) {
            return component;
          }
        }
        return defaultComponent;
    }
}

ここでinitChildComponentメソッドに着目すると、componentを追加(== 拡張)する際に、コードの変更(== 修正)が必要なくなったことがわかります。つまり、initChildComponentレベルで、拡張に対して開いているということになります。なお、クラスレベルでオープン・クローズドの原則を担保する場合は、dispatch関数を別クラスに定義することで、さらに責務を分離できるでしょう。 また、例えばここで getComponentsメソッドを動的にあるディレクトリ配下のcomponentを取得するような処理にするようにすれば、ファイルを追加するだけで上記のコードをオープン・クローズドの原則に従った形で拡張することができます(実際はそこまでせずに、getComponentsの関数の中は追記可とする設計でも、ある程度は結合度を下げられるのでそれで十分という場合もあるかと思います)。

リスコフの置換原則

リスコフの置換原則は、派生型(子クラス)は基本型(親クラス)と置換可能でなければならないという原則です。子クラスで不用意に親クラスのメソッドを上書きしたりすると、子クラスと親クラスが置換不可能になってしまい、クラスのis-A関係が崩れてしまいます。

私達のコードではリスコフの置換原則に反したコードは見つかりませんでしたが、今後も注意してコーディングしていきたいです。

インターフェース分離の原則

インターフェース分離の原則は、クライアントに、クライアントが利用しないメソッドへの依存を強制してはならない。という原則です。 例えばメソッドAとメソッドBがあるインターフェースを利用するクライアントが常にメソッドAしか実行しない場合、メソッドBへの依存は無駄となってしまいます。

export interface IApiPageLogic {
    reverseGeocoding(
        locationIds: string[],
        serviceTypeId?: string,
        pattern?: MasterApiModels.AreaFetchingPatterns
    ): Promise<LocationInfoWithRelaseFlag[]>;
    getStoreAccessToken(
        storeId: string,
        platform: TwilioPlatforms
    ): Promise<CapabilityToken | undefined>;
}

上記のインターフェースは地理情報の処理メソッドと認証情報の取得メソッドが同居しています。そして、これれらのメソッドを必要としているクライアントも別々のクラスです。

この場合は、

export interface IGeocodingLogic {
    reverseGeocoding(
        locationIds: string[],
        serviceTypeId?: string,
        pattern?: MasterApiModels.AreaFetchingPatterns
    ): Promise<LocationInfoWithRelaseFlag[]>;
}


export interface IAuthTokenLogic {
    getStoreAccessToken(
        storeId: string,
        platform: TwilioPlatforms
    ): Promise<CapabilityToken | undefined>;
}

このように、ユースケースを考慮して別々のインターフェースとして分離したほうがより良い設計かと思います。

依存関係逆転の原則

依存関係逆転の原則は、 上位レベルのモジュールは下位レベルのモジュールに依存すべきではない。両方とも抽象に依存すべきである。 という原則です。

くらしのマーケットのNode.jsのコードでは、現在DIの仕組みを取り入れており、上位のモジュールは下位のモジュールのインターフェースに依存するようになっています。

@Dependency.register()
export class CalendarManager implements ICalendarManager {
    static $inject: string[] = ["CalendarApi"];
    constructor(private calendarApi: ICalendarApi) {}
    // メソッド定義
}

例えばこのCalendarManagerクラスは、CalendarApiをインジェクションしていますが、その機能には ICalendarApiインターフェース経由でしかアクセスできません。この場合、コンストラクタ引数のcalendarApiがどのように具体的に実装されているか、CalendarManagerクラスは知ることはできません。このようにして、上位モジュールと下位モジュールの結合度を下げることができます。

まとめ

以上、くらしのマーケットのコードを例にして、SOLID原則の紹介をしてみました。これからも設計を意識しながら、保守しやすいコードを目指して開発していきたいです。 くらしのマーケットのシステムに興味がある方はコーポレートサイト https://www.minma.jp/までお気軽にご連絡ください。

「話だけ聞きたい!」ってことでオフィスに遊びにきていただくことも可能です。 話を聞いてみる / みんなのマーケット株式会社

参考

PythonでMeCabとBeautifulSoupをおいしくいただきたくなったので簡単なクローラーを作ってみた

こんにちは、エンジニアのつづきです。

最近、社内でちょくちょくSEOが。。。
とか、
クローラーが。。。

とかいう話を耳にするようになりました。

クローラー」いい響きですね。

鍋も美味しいシーズンになってきましたから、Pythonを使ってMeCabとBeautifulSoupをおいしくいただくのもいいですね。(全く関係ない)

https://user-images.githubusercontent.com/40745943/50324643-79627280-0523-11e9-954e-6b49857291ee.JPG (オフィスにて会社の皆さんとやった火鍋)

ということで、今回は、Pythonで簡単なクローラーを作ります。

そもそもクローラーとは、

WEB上のファイル(HTML文書だけでなく、画像・PDFまで含む全般)を収集するためのプログラム[1]

らしいです。

では早速作っていきましょう。
用意するものは、以下の通りです。

Python 3.5.0
MeCab
Beautifulsoup4
urllib

まずは、どこかのWebサイトにアクセスするコード[2]を以下に記述します。

import urllib.request
def getHTML(url):
    return urllib.request.urlopen(url)

とっても簡単ですね。
次は、WebページからHTMLを取得するコード[3]を以下に記述します。

from bs4 import BeautifulSoup
def soup(html):
    return BeautifulSoup(html, "html.parser")

これもまた3行でできてしまいました。
取得できた結果は、以下のようになります。

<中略>
<!--
                                ....JgNNMMMMMMMN;
                           ..gNMMMMMMMH""""77TMM#
                      ..&MMMMMH""!            dMM[
                   .(NMMMB"!                  ,MM#
                ..MMMM"!                       dMM;
              .gMMMY`            .+g,    .gg,  (MMb
            .dMM#=               MMMM:  .MMMM~  MMN.
           (MM#^                  ?7` ` ` ?!    JMM]
         .MMMY                                  .MMN
        .MM#!             ..JJ-..............((+NMMM:
       .MM@              .MMMMMMMMMMMMMMMMMMMMMMMB"!
      .MM@             .dMMD
     .MM#             .dMMC
     jMM%             dMM%                                  < 仲間募集!
    .MM#             .MMF                                       http://www.minma.jp/recruit/
    .MMb             JMM\
    .MMb             (MML
    .MMN              dMMm.                   ..gMM]
     dMM]              ?MMMNgJ...      ...(JgMMMMMMb
      WMM,               _TMMMMMMMMMMMMMMMMMMB"!.MMb`
       TMMm.                  _??77"777?!`      .MMb
        (MMMm,                               `..(MMb
          ?WMMNa...                    ....&NMMMMMB!
             ?WMMMMMNNgggJJ(((JJ&gggNMMMMMMM#"=!
                 ?7TWHMMMMMMMMMMMMMHB""7!
-->
<中略>

次は、取得したHTMLから<p>を取得してくるコードを以下に記述します。

def getPTags(soup):
    return soup.find_all("p")

今度は2行でできてしまいました。
結果は以下のようになります。

<span class="tag">特集</span></p>
<p class="text-sub">年末大掃除</p>
<p class="ttl">がんばらない大掃除<br/>のススメ2018</p>
<span class="tag">特集</span><span class="new">NEW</span></p>
<p class="text-sub">かゆいところに手がとどく</p>
<p class="ttl">引越し関連サービス</p>
<span class="tag">マガジン</span></p>
<p class="text-sub">利用者の声</p>
<p class="ttl">くらしのマーケットとメルカリで引越しが完結した!</p>
<span class="tag">マガジン</span></p>
<p class="text-sub">利用者の声</p>
<p class="ttl">引越しの救世主!?直前予約でもスムーズに引越せた!</p>
<span class="tag">マガジン</span></p>
<p class="text-sub">利用者の声</p>
<p class="ttl">最初は友人の紹介で。僕も友人に紹介してます!</p>
<以下略>

次は、取得した<p>から、文章するコード[3]を以下に記述します。

def getStrings(tags):
    string = ""
    for p_tag in p_tags:
        string += re.sub(r'<.*?>','', str(p_tag)) + “\n”
    return string

上記のコードでは、<.*?>という正規表現でタグのみを空の文字列と入れ替えています。
結果は以下のようになります。

特集
年末大掃除
がんばらない大掃除のススメ2018
特集NEW
かゆいところに手がとどく
引越し関連サービス
マガジン
利用者の声
くらしのマーケットとメルカリで引越しが完結した!
以下略

次は、MeCabを使って、形態素解析をしてみましょう。
しかし、そもそも形態素解析とはなんでしょうか?

コンピューターの自然言語処理の一。与えられた文章を、辞書データなどを用いて形態素の単位に区切り、品詞を判別する処理。[4]

らしいです。
では早速上記で得られた結果を用いて形態素解析をしてみましょう。
MeCabを利用した形態素解析を行うプログラムを以下に記述します。

    mecab = MeCab.Tagger ("-Ochasen")
    mecab_string = mecab.parse (string)
    for ms in mecab_string.split("\n"):
        word = ms.split("\t")[0]

結果は以下のようになります。

特集
年末
大
掃除
がんばら
ない
大
掃除
の
ススメ
2018
特集
NEW
かゆい
ところ
に
手
が
とどく
引越し
関連
<以下略>

どうやら、Pythonを使ってMeCabとBeautifulSoupをおいしくいただくことができました。

今回ご紹介したようなクローラーを使えば、Webページ上のデータを取得して、様々な分野のトレンドなどの情報収拾に利用できたりするかもしれません。
同時に複数のページを解析するのはどうするの?とか、HTMLの解析で複数のタグに囲まれている要素はどうするの?とかはまた別のお話

最後に

私たちテックチームでは「くらしのマーケット」を一緒に作る仲間を募集しています。

Pythonを使って開発をしたいな」
「火鍋一緒に食べたいな」

ご興味のある方、ご応募お待ちしております。 http://www.minma.jp/careers/

「話だけ聞きたい!」ってことでオフィスに遊びにきていただくことも可能です。 話を聞いてみる / みんなのマーケット株式会社

お気軽にご連絡ください!

参考文献

[1] https://www.seohacks.net/basic/terms/crawler/
[2] https://docs.python.jp/3/howto/urllib2.html
[3] https://qiita.com/itkr/items/513318a9b5b92bd56185
[4] https://kotobank.jp/word/%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90-3115

An adventure on getting in a Japanese company

Hi, I’m Vincent Biñas. A new hire working as a Junior Developer in Minma Inc. As my first blog post, I will talk about how I got work here.

https://user-images.githubusercontent.com/40745943/49488906-f509c180-f88b-11e8-862d-9b4012883b21.jpg

Why Japan?

I have always dreamt of working abroad, with promises of a better quality of life, good work-life-balance, and a new point of view brought on my experiencing a new culture. I have considered a number of countries near Asia. I have found Japan to be the best fit. The people, the scenery, everything was welcoming when I first visited. I have considered Singapore, where its close vicinity to the Philippines, and English language a plus. But in contrast to Japan opening its doors more to foreign workers, Singapore has stricter laws. Hong Kong was also in my list, but the high standard of living makes it nigh impossible for me to afford. Japan’s rent may also be high, but in certain places, they have great value. Weiging the pros and cons, I decided to go to Japan.

Finding out if I am up for this

Working my way into Japan, I earned enough money in the Philippines for a year. Given the uncertainties, I would like to experience living for the first time abroad for a little bit before committing. That’s when I decided that I will study Japanese in Japan for a year before finding work.

Getting into a Japanese language school has its hardships as well, but that’s for another blog post in my opinion. But long story short, I came to Tokyo, what I earned was unfortunately not enough. I found a freelance job that got me enough money to afford my stay here, and it was also a good experience that would help me later on. 3 months before my graduation, I applied and got into an interview from one of the game companies I follow.

Rollercoaster ride of emotions

I got rejected. A game company I was aiming for gave me the sad news via email. I pretty much expected the result. I breezed through the first casual interview in English, but went bananas over the second interview, which was in Japanese. Their HR, however, referred me to a recruiting agency. I was surprised when the assigned recruiter called me through my phone. After registering and going through a number of steps, the volley of referrals came in like a flood. To be completely honest, I never really had an entry there that I was interested in.

There were only car companies, medical systems, and other technical things far from what I love; games or websites! It was rather hard to choose. One had caught my eye though, Minma Inc. The company outline was quite different from the others, I should say a little more lenient, in which, feels like the specs connect to devs more. Other companies expect a lot from their applicants, not to mention how I was still in a slump from almost making it to a game company I follow. Days turn to weeks, and out of the three companies I applied to, only one proceeded with the interview. You’ve guessed it: Minma Inc.

The recruiter gave me some resources about the company, including interviews of the CEO. Given my Japanese level at that time, I had little knowledge on such unfamiliar terms, and ended up google translating most of the things written. Google translate does wonders to perfectly structured Japanese, either turning them into unintelligible pieces of English, or changing their meaning altogether. I managed to get by understanding it due to spending a year in a Japanese language school, but losing some of the nuances that would’ve shed light on what this company exactly is. Expecting a very strict atmosphere, I had mentally prepared myself, including practicing Japanese to get by. What I saw when I went to the office was the total opposite.

https://user-images.githubusercontent.com/40745943/49488672-04d4d600-f88b-11e8-998d-2e96fe22a3dc.jpg

People wearing casual clothes, no dividers, a hangout, games,

https://user-images.githubusercontent.com/40745943/49488675-08685d00-f88b-11e8-9fdf-6dae4a80b16a.jpg

https://user-images.githubusercontent.com/40745943/49488774-69903080-f88b-11e8-8af3-46879c891073.jpg

and CATS.

https://user-images.githubusercontent.com/40745943/49488678-0b634d80-f88b-11e8-8a43-f56da0b68f22.jpg

https://user-images.githubusercontent.com/40745943/49488681-0e5e3e00-f88b-11e8-90f2-8139807d21b5.jpg

I was very surprised. The first interview went by pretty well, I had little misunderstandings, and managed rather well with the questions. I passed this interview.

With my newfound confidence, I gave myself a pat on the back, and prepared for the second interview. The recruiter gave me the information about what the second interview will contain: a technical interview, and an SPI test. SPI Test? I wonder what that is? A quick google search gave me a horror story. Advanced Japanese questions that even natives have a hard time, and not to mention math questions and a personality test! I would want to study, but a leeway of 1 week was pretty unforgiving. My “newfound confidence” by this time disappeared in a jiffy. Going in for the second interview, I was a nervous wreck. I came in and found that somehow, people were surprised I’m here. I was equally confused. “How did they not expect an interviewee coming today?” I wondered. It turns out, I was a day early. I bet I turned beet red in embarrassment, they called the recruiting agency to confirm if the date was mistaken or not. Though the email confirmed that I was at fault. Wednesday, 水曜日 was written there, and it was Tuesday, 火曜日. Given how strict the Japanese custom of being punctual is, it made a nervous wreck of me even worse.

“My chances here are close to nil by now” My mind tells me. Coming in the actual date and taking the test did not really make me any more confident. The SPI was hard enough that I give myself a solid 2/10 for my performance in that test. The technical question was doable in my opinion, but what exactly what I must do is shrouded in Japanese largely unheard of by me. I managed to get an outline of the solution out, but I was not able to answer the follow-up question. Walking home with my head low, I pretty much was back on recruiting sites searching for other possible jobs. I was definitely not passing that one! God knows when I would find a company that has a good atmosphere, and cats.

With my finances and time spread thin, I had no choice but to go for another 6 months. I paid my tuition and hoped for the best for another 6 months. The recruiter that has always been in contact with me called. I expected the bad news, I couldn’t be anymore prepared to take it. But lo and behold, I somehow passed the second interview. Surprised and happy is an understatement to what I felt that time, but of course, at the back of my mind, I always know that there is still a third interview. My friends were telling me that the third interview is already a go sign. That gave me a confidence boost, but I still have caution.

The third interview was pretty much a breeze. All there was, was a talk with the CEO, and a company tour. It got me excited. It was only a matter of weeks when I got my offer letter. I finally have a job!

Conclusion

In the end I do not know if it’s by chance or not that I got here, but I am certain that it is because I carried on with a crazy dream that got me to where I am. At present, Minma Inc is a very good place to work! Free drinks and snacks, lots of leisure facilities, and awesome workmates. I have to say though, that cats here are definitely best perk. One month in and I still have ways to go when it comes to dev skills and communication, but everyone’s a champ for being great mentors, not to mention how the company gives every person abroad optional Japanese language lessons. Minma Inc is hiring by the way, so if you are interested, stop by our website at http://www.minma.jp/careers.

話を聞いてみる / みんなのマーケット株式会社

あとで困らないCSS設計 〜クラス名の付け方〜

はじめに

こんにちは。
みんなのマーケットのテクノロジー本部でデザイナーをしているラミレスです。
本日は「あとで困らないCSS設計」の第一回目として、「クラス名の付け方」についてお話したいと思います。

クラス名を付けるのは意外と大変

私はクラス名を付ける時に非常に悩みます。
「このクラス名をつけて、あとで誰かが困ることはないかな?」
「他で利用しているクラス名ではないかな?」

などなど。

特に子要素、孫要素など、要素が似ていると似たような名前になってしまったりして、「似たような名前が多いけど、この名前で伝わるかな?」と不安になったりしています。

そこで、今回は「できるだけ簡単」に「みんなが困らない」クラス名を付ける方法を調べてみました。

クラス名の悪い例

まずはこれまで遭遇して私が困ったクラス名(クラス名の悪い例)を紹介していきます。

(1)相対的
例えばクラス名の中に ”big” と記載があったとしましょう。

しかし、このクラス名はあまりよろしくありません。 理由は、

・大きいって誰から見て、何と比較して大きいの?
・今後もっと大きいものが出てきたらどうするの?

という問題がつきまとうためです。

例としては下記が挙げられます。

悪い例:
.big-img
.small-btn

(2)HTML要素やDOMツリーに依存している
例えば ”h2-ttl” のようなHTML要素に依存するクラス名は、「h2がh1に変化した場合に矛盾する」などの問題が生じてしまいます。
また、DOMツリーの構造に依存している “body-div-p” のような場合も同様で「pがspanに変化した場合に矛盾する」という問題が生じます。

そのため、HTML要素やDOMツリーに依存しないようなクラス名をつけましょう。

悪い例:
.body-div-p

(3)定義されていない省略形を使用

クラス名をつける時、「ボタンはbtnにしよう」とか「タイトルはttlにしよう」というようなルールや定義を組織内で決めている場合があります。
今回の悪い例は、その定義なしに勝手に省略形にして、本人以外が何を指しているかがわからないというケースです。

例えば、

.button-l
.pnNext

など。

なぜ困るのかというと、コードだけ見たときにクラス名からどこに指示を出しているのか瞬時に判断できなくなってしまうためです。
(実際の表示と照らし合わせながら、「ここのこと指しているのか」と想像しなければならない訳です)

BEMという命名規則

上記に挙げたような、「クラス名の悪い例」にならないようにするにはどうしたらいいか?
代表的な命名規則にBEMというものがあります。

まずBEMについて簡単に説明します。
BEMはBlock Element Modifierの略で、BlockとElementとModifierをルールに従ってつないだものです。

(1)Block
多くのWebページは、ヘッダー、メニュー、サーチ、フッターなどといったパーツの集まりで構成されています。BEMでは、これらのパーツをBlockと呼びます。

f:id:curama-tech:20181130161055p:plain
Blockのイメージ

例:
header
head
contents
side
sidebar
menu
search
footer

(2)Element
1つのBlockは、Block自身を構成する部品のような(Blockを構成している)ものを有しています。 例えば、検索フォームBlockなら、「入力フィールド」と「ボタン」の2つの部品で構成されています。これをElementと呼びます。

例:
ttl
body
detail
more
submit
input
btn
menu-item
list-item

(3)Modifier
同じBlockであっても、カレント状態であったり、通常の状態とエラー状態などで異なる装飾を設定する場合があります。そうした装飾に関する調整を、Modifierと呼びます。

例:
current
disabled
checked
fixed

以上がBEMの簡単な説明です。

BEMには

・__(アンダーバー2つ)--(ハイフン2つ)で接続する
・Element-Modifier間は _(アンダーバー)で接続する
・Modifierは状態の名前と値をセットにする

など、細かいルールがありますが、今回はこのBEMを応用してすぐに使える命名規則を私なりに考えてみました。

BEMの中で、Blockはどこの部分でも必ずある要素ですが、ElementとModifierは要素によって存在しない場合もあります。

そこで、今回決めた命名規則は以下の4つです。

・Blockを記載する
・ある場合はElementを記載する
・ある場合はModifierを記載する
・全てをハイフンで接続する

例えば、
「ヘッダー内にあるマイページに遷移するためのボタン」であれば

.header-mypage-btn

「コンテンツ内のソートを切り替えるタブ」であれば

.contents-sort-tab

などの名前を付けることができます。

今回挙げたのはあくまで例ですが、社内や同じ組織の人と命名規則を決めて運用することで、あとで誰かが困ることがなくなるのではないでしょうか。
(孫要素をどうするかや、Blockに使う名前を固定するなど、細かいルールも決めておくと、より簡単にクラス名を考えることにできると思います)

命名規則を決めると下記の利点を享受できるはずです。

・クラス名を付けるのに時間がかからない
・誰が見ても何を意味しているかわかる
・同じクラス名になりにくい

最後に

私たちテックチームでは「くらしのマーケット」を一緒に作る仲間を募集しています。ご興味のある方、ご応募お待ちしております!
http://www.minma.jp/careers/

「話だけ聞きたい!」ってことでオフィスに遊びにきていただくことも可能です。 話を聞いてみる / みんなのマーケット株式会社

参考文献

https://ferret-plus.com/7808
https://app.codegrid.net/entry/bem-basic-1#toc-3
https://qiita.com/jiroor/items/17c994bd3b18c83d746d
https://en.bem.info/