2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS】CDKとcdkd、一体どのくらい速くなる?実測で比べてみました

2
Last updated at Posted at 2026-09-11

どうもこんにちは。

先日ですね、JAWS-UG CDK支部のイベントにLT登壇させていただきまして、CDKとcdkdのデプロイ時間がどのくらい違うのかっていう内容をお話しさせていただきました。(よーすけさんと被ってしまいましたが...w)

登壇資料はこちらです。ご査収くださいませ。

ぬるっと本題に入りますね。

自分は、普段の開発(個人)ではAWS CDKを使っています。AWS CDKってなんぞやっていう方向けにお話ししますと、インフラをコードで定義する(IaC)手法というものがございまして、これを実現するためのツールがいくつかございます。AWS CDKはそのうちの1つです。CloudFormationもその1つでして、Terraformもそれに該当します。

世間一般的によく使われているのはTerraformですね。(TerraformはAWSだけではなくて、Google CloudやMicrosoft Azureにも対応している優れものです。)

Terraformは、世の中でよく使われているプログラミング言語(Ruby, Python, TypeScript, JavaScript etc...)で書くツールではなく、HCLという独自言語を用いてインフラコードを書きます。

触ってみていると、GoとJSONを足して2で割ったような言語のように感じますね。オブジェクト指向言語というよりは宣言型言語って感じで、PythonやTypeScriptとは違う雰囲気で面白い。。。

AWS CDKでは、AWSリソースをTypeScriptやPythonコードで定義し、その定義からCloudFormationを通じてデプロイできるツールです。

そんなこんなで、電車でXを眺めていた時、cdkdというツールの存在を知りました。Xをみていると「cdkdはデプロイが爆速になる!」というようなツイートを見かけたりしたので、触ってみるがてら計測してみようかなということで、デプロイ時間を計測してみました。(ついでに削除時間も計測してみました。)

ということで、記事の本題に入っていきましょう。(前置きが長かったですね。)

cdkdとは

cdkdは、CDKのコードをそのまま使いながら、CloudFormationを経由せずにAWSリソースをデプロイするCLIです。CDKが生成したCloudFormationテンプレートの内容を解析し、AWS SDKまたはAWS Cloud Control APIを使ってリソースを操作します。開発中のデプロイを短いサイクルで繰り返すことを主な用途として位置付けています。

ここで注意点なのが、CloudFormationを経由しないだけであって、CloudFormationテンプレートは作成されます。
CDKの場合は、CloudFormationテンプレートを生成して、それをCloudFormationデプロイするという動きをします。cdkdはCloudFormationデプロイではなく、CloudFormationテンプレートを生成してそれを元に、AWS SDKまたはAWS Cloud Control APIを使ってデプロイするという動きをします。

cdkdではCloudFormationスタックを作らず、S3に保存するstateファイルとAWS API照合を使って、リソースごとの依存順序と状態を管理します。そのため、CloudFormationのスタック作成や変更セット、イベント監視を経由する場合とは、CLIが終了するまでの時間や状態確認の方法が異なります。どのリソースを操作できるかはcdkdの対応状況に依存するため、利用前に対象サービスを確認する必要があります。

cdkdでは、リソースのデプロイ完了=deployコマンドの終了時間 ではないので、注意が必要ですね。

CDKとcdkdの違い

今回の比較対象は同じTypeScript定義から作成したAWSリソースです。前述したように、CDK側はCloudFormationスタックを通じて操作し、cdkd側はリソースごとのAWS APIを直接呼び出します。

初心者が最初に押さえる違いは、デプロイ経路、状態管理、完了待機、対応リソース、利用場面です。

観点 AWS CDK cdkd
デプロイ経路 cdk deployを起点にCloudFormation経由でAWSサービスを操作します cdkd deployを起点にAWSサービスAPIを呼び出します
状態管理 CloudFormationのスタックとイベントで管理します S3に保存するstateファイルとAWS API照合で管理します
完了待機 通常のcdk deployではCloudFormationのスタック完了を待ちます。--expressでは安定化を待たずに終了します 既定では一部の安定化を待ちます。--full-waitでは追加の安定化も待ち、--no-waitでは非同期リソースの安定化を待ちません
削除経路 cdk destroyを起点にCloudFormation経由で削除します cdkd destroyを起点にAWSサービスAPIを呼び出します
リソース対応 CloudFormationが対応するリソースを扱えます cdkdの対応状況に依存します
向き・不向き 変更セット、ロールバック、ドリフト検出、履歴、レビューやガバナンスを重視する場面の候補になります 開発・テストなど、CLI終了後にreadinessを別途確認できる高速な反復の候補になります

CDKの基本動作としては、まずCloudFormationテンプレートを作成し、CloudFormationが依存関係を解決して各サービスを操作します。通常のCDKデプロイではchange-set方式で変更セットを作成しますが、--method directを指定した場合は変更セットを作成しません。この経路には、CloudFormationによるオーケストレーション、変更セット、イベント監視、リソースの安定化待ちが含まれます。

「CloudFormationによるオーケストレーション」、「変更セット」、「イベント監視」、「リソースの安定化待ち」が計測時間の差の一部になり得ますが、今回の計測だけでは、どの要素がどれだけ寄与したかまでは分かりません。
この中のどれかが時間がかかっているんだろうなぁという推測までです。

cdkdでは、CloudFormationテンプレートからCloudFormationスタックを作らず、cdkdが依存順序を管理してAWS SDKまたはAWS Cloud Control APIによる操作を発行します。リソース単位で状態を確認するため、CloudFormationのスタック単位の進行確認とは異なる時間軸になります。

一方で、CDKはCloudFormationが持つ変更セット、ロールバック、ドリフト検出、スタック単位の履歴を利用できます。cdkdの速度だけを見て移行判断をするのではなく、運用で必要な状態管理・障害復旧・レビュー手順まで含めて判断する必要があります

今回の計測について

計測条件

時間軸は分けて記録しました。

時間軸 記録方法
CLI終了まで deployまたはdestroyコマンドを開始してから、コマンドが終了するまでの時間です。macOSのtimeコマンドが出力する実時間(real)を記録します。この時点では、リソースが利用可能または削除済みとは限りません
利用可能まで deployコマンドを開始してから、シナリオごとに定めたサービス別の確認がすべて成功するまでの時間です。例えば、DynamoDBがACTIVEになったことや、HTTP APIが200を返すことを確認します
削除確認まで destroyコマンドを開始してから、削除前に記録したリソースをAWSのサービス別APIで確認し、検査可能なリソースが消失するまでの時間です

前述している削除確認ループとは、独自に私が実装した処理です。destroyコマンドの終了後、削除確認処理を実行して、事前に取得したリソース一覧をもとに、サービス別のAWS APIで検査可能なリソースが消失したことを確認します。

項目 条件
リージョン ap-northeast-1
実行環境 macOS arm64、Node.js v24.20.0
AWS CDK CLI v2.1139.0
aws-cdk-lib v2.267.0
cdkd v0.285.7
計測方法 macOSのtimeコマンドを使い、コマンド開始から終了までの実時間(real)を記録します。作成・削除は各モード1回ずつ計測します
通常シナリオの実行順序 baselinewideserverlessでは、作成、利用可能状態の確認、変更なしの再デプロイ(noop deploy)、設定を変更した再デプロイ(update deploy)、更新後の利用可能状態の確認、削除対象の記録、削除の順に実行します
asyncの実行順序 作成後に利用可能状態を確認し、確認用ファイルをS3から削除します。その後、削除対象を記録して、すぐに削除を開始します
削除確認 削除前に記録したリソースのうち、サービス別APIで検査可能なものが消失するまで確認します。destroyコマンド終了後の確認処理は最大1200秒(20分)ですが、個々のAWS API呼び出しにかかる時間はこの上限に含まれない場合があります

比較トラックは次のとおりです。

比較目的 CDK cdkd 読み方
完了待機 default full CLI終了と利用可能までを比較する主トラック
既定モード default default 各CLIの既定モードとして比較する
早期終了 express no-wait CLIが戻る早さの比較であり、利用可能時間の比較ではない
補助計測 direct 対応なし 単独の参考値であり、主比較の速度比には使わない

asyncではcdk-directを実行しません。

実行するコマンド

各モードのdeployに次のコマンドを組み立てます。<scenario>にはbaselinewideserverlessasyncのいずれかを指定します。<benchmarkRunId>は実行全体のIDを指定します。<deploymentId>は反復・モード・シナリオから生成した値を指定します。

CDKのスタック引数は合成アプリのconstruct IDであるBenchmarkStackです。cdkdのスタック引数はstackNameForが生成する物理スタック名であるBenchmark-<scenario>-cdkd-<deploymentId>です。

# CDK default
npx cdk deploy --require-approval never BenchmarkStack --region ap-northeast-1 --context scenario=<scenario> --context manager=cdk --context benchmarkRunId=<benchmarkRunId> --context deploymentId=<deploymentId> --context variant=base

# CDK direct
npx cdk deploy --method direct --require-approval never BenchmarkStack --region ap-northeast-1 --context scenario=<scenario> --context manager=cdk --context benchmarkRunId=<benchmarkRunId> --context deploymentId=<deploymentId> --context variant=base

# CDK express
npx cdk deploy --express --require-approval never BenchmarkStack --region ap-northeast-1 --context scenario=<scenario> --context manager=cdk --context benchmarkRunId=<benchmarkRunId> --context deploymentId=<deploymentId> --context variant=base

# cdkd full
npx cdkd deploy --full-wait --yes Benchmark-<scenario>-cdkd-<deploymentId> --context scenario=<scenario> --context manager=cdkd --context benchmarkRunId=<benchmarkRunId> --context deploymentId=<deploymentId> --context variant=base --state-prefix cdk-vs-cdkd

# cdkd default
npx cdkd deploy --yes Benchmark-<scenario>-cdkd-<deploymentId> --context scenario=<scenario> --context manager=cdkd --context benchmarkRunId=<benchmarkRunId> --context deploymentId=<deploymentId> --context variant=base --state-prefix cdk-vs-cdkd

# cdkd no-wait
npx cdkd deploy --no-wait --yes Benchmark-<scenario>-cdkd-<deploymentId> --context scenario=<scenario> --context manager=cdkd --context benchmarkRunId=<benchmarkRunId> --context deploymentId=<deploymentId> --context variant=base --state-prefix cdk-vs-cdkd

destroyはdeployモードに依存せず、次の共通コマンドを使います。CDKには--forceを付け、cdkdには--yes--state-prefix cdk-vs-cdkdを付けます。destroyには--express--method direct--full-wait--no-waitを付けません。

# CDKの全deployモードで共通
npx cdk destroy --force BenchmarkStack --region ap-northeast-1 --context scenario=<scenario> --context manager=cdk --context benchmarkRunId=<benchmarkRunId> --context deploymentId=<deploymentId> --context variant=<variant>

# cdkdの全deployモードで共通
npx cdkd destroy --yes Benchmark-<scenario>-cdkd-<deploymentId> --context scenario=<scenario> --context manager=cdkd --context benchmarkRunId=<benchmarkRunId> --context deploymentId=<deploymentId> --context variant=<variant> --state-prefix cdk-vs-cdkd

ここで示した--contextの値、スタック引数、引数の順序はscripts/benchmark.tscommandArgumentsが生成する形に合わせています。cdkdのリージョンはコマンド引数ではなく環境変数でap-northeast-1に設定します。

シナリオ

上記でシナリオってなんぞやって思われた方がいらっしゃるかと思いますが、以下のことです。
1リソース単位で計測するかも悩んだところなんですが、多少ユースケースに近づいていた方がイメージつきやすいかなぁと思って、いくつかに任意のリソースを含んだシナリオを用意して計測することにしました。その上で、単純な基本構成からサービス数、連携、安定化待ちの違いによっての時間の変化を確認したいと考えたため、以下の4つのシナリオになっています。(EC2, ECSについても計測したいとは思っているんです。)

シナリオ 実装上の主リソース 利用可能の判定
baseline S3バケット、SQSキュー、DynamoDBテーブル S3、SQS、DynamoDBをサービス別APIで確認します
wide S3、SQS、SNS、DynamoDB、SSM Parameter Store、CloudWatch Logsを各5個 6サービス計30リソースをサービス別APIで確認します
serverless HTTP API、Lambda 3個、DynamoDB、SQS、SNS、EventBridgeルール、CloudWatch Logs 3個 Lambda、DynamoDB、EventBridge、HTTP APIを確認します。SQS、SNS、CloudWatch Logsの存在確認はreadiness probeの対象外です
async 2 AZのVPC、NAT Gateway 1個、プライベートLambda、S3、CloudFront NAT Gateway、Lambda呼び出し、CloudFront、health objectを確認します

各シナリオは上記の比較トラックに従って実行しました。asyncはCDKのdefaultexpressとcdkdのfulldefaultno-waitを対象にし、cdk-directは実行しません。

シナリオ 選定意図
baseline S3、SQS、DynamoDBという比較的低コストで独立した基本サービスを使い、最小構成のライフサイクルを基準にします
serverless HTTP APIと3つのLambdaに加え、DynamoDB権限、SQSイベントソース、SNSサブスクリプション、EventBridgeターゲットを含め、複数サービスの連携設定とIAM依存を含む構成にします
wide S3、SQS、SNS、DynamoDB、SSM Parameter Store、CloudWatch Logsを各5個作り、多数の独立リソースを同時に扱う構成にします
async 2 AZのVPC、NAT Gateway、private Lambda、S3、CloudFrontを組み合わせ、作成後のサービス安定化を長く待つ構成にします

サービス単位の利用可能状態の確認について

deployコマンドを実行して、スタック出力を取得した後、シナリオごとに次のAWS CLIまたはHTTP probeを実行します。probeが成功した時点を「利用可能」と記録します。これはリソースの存在・状態・単純な応答を確認する独自の実装処理です。

シナリオ サービス 利用可能判断
baseline S3 head-bucketが成功
baseline SQS get-queue-attributes --attribute-names Allが成功
baseline DynamoDB describe-tableTableStatusACTIVE
wide S3、SQS、SNS 各5個についてそれぞれhead-bucketget-queue-attributesget-topic-attributesが成功
wide DynamoDB 各5個のdescribe-tableACTIVE
wide SSM Parameter Store 各5個のget-parameterが成功
wide CloudWatch Logs 各5個のdescribe-log-groupsに対象ロググループが含まれる
serverless Lambda 3個すべてのget-function-configurationActive
serverless DynamoDB describe-tableACTIVE
serverless EventBridge describe-ruleの状態がENABLED
serverless HTTP API APIエンドポイントへのHTTP応答が200
async NAT Gateway describe-nat-gatewaysの状態がavailable
async private Lambda lambda invokeの応答にok: trueが含まれる
async CloudFront get-distributionの状態がDeployedで、benchmark-health.txtへのHTTP応答が200

serverlessでは、SQSへメッセージを投入してLambda処理を確認する検査、SNSからLambdaへの配信確認、EventBridgeルールの実発火確認は実装していません。

いざ結果発表〜!!!

はい、結果の発表です。

デプロイ時間の結果

以下は、全モードについて1回ずつ計測したコマンドを開始してから、コマンドが終了するまでの時間です。単位は秒で、小さいほどCLIが早く終了します。

発表対象の主比較は、比較トラックの表を参照いただけますと幸いです。

シナリオ CDK default CDK direct CDK express cdkd full cdkd default cdkd no-wait
baseline 82.22 79.43 85.51 14.68 17.23 14.34
wide 86.33 79.22 81.59 17.23 19.01 15.53
serverless 197.08 115.90 120.07 19.23 18.72 19.89
async 312.89 - 296.47 161.51 134.55 21.58

シナリオ: baseline

baselineでは、CDK default、CDK direct、CDK expressが82.22秒、79.43秒、85.51秒、cdkd full、cdkd default、cdkd no-waitが14.68秒、17.23秒、14.34秒でした。完了待機、既定モード、早期終了のいずれの対応トラックでも、この1回の計測ではcdkdのCLI終了時間が短くなりました。完了待機トラックのCDK default÷cdkd fullは約5.6倍、既定モードでは約4.8倍です。

シナリオ:serverless

serverlessでは、CDK defaultが197.08秒、CDK directが115.90秒、CDK expressが120.07秒でした。cdkd full、cdkd default、cdkd no-waitは19.23秒、18.72秒、19.89秒です。対応する3つのトラックすべてで、この1回の計測ではcdkdのCLI終了時間が短くなりました。CDK directとCDK expressの差は4.17秒で、同じCDK側でもモードごとの値が異なります。この差がどの処理に由来するかは、今回の計測だけでは特定しません。完了待機トラックのCDK default÷cdkd fullは約10.2倍です。

シナリオ:wide

wideでは、CDK defaultが86.33秒、CDK directが79.22秒、CDK expressが81.59秒、cdkd full、cdkd default、cdkd no-waitが17.23秒、19.01秒、15.53秒でした。baselineと同じく、対応する各トラックでcdkdのCLI終了時間が短い結果です。これは追加したシナリオでの観測であり、サービス数が増えた場合にも同じ差が続くと一般化するものではありません。

シナリオ:async

asyncでは、CDK defaultが312.89秒、CDK expressが296.47秒、cdkd full、cdkd default、cdkd no-waitが161.51秒、134.55秒、21.58秒でした。cdkd no-waitの21.58秒はCLI終了までの値であり、利用可能になる速さを表しません。asyncのCLI終了、利用可能判断、削除確認は後の節で分けて説明します。

削除コマンド時間の結果

以下はdestroyのコマンドを開始してから、コマンドが終了するまでの時間です。単位は秒で、小さいほどCLIが早く終了します。表の列は、作成時に対応するdeploy条件を識別するために残していますが、destroyコマンド自体はdeployモードに依存しない共通コマンドです。

シナリオ CDK default CDK direct CDK express cdkd full cdkd default cdkd no-wait
baseline 73.47 73.86 72.07 9.80 10.31 11.19
wide 73.80 74.21 74.16 13.50 10.08 10.66
serverless 88.21 89.52 95.92 10.08 11.49 11.81
async 1269.91 - 204.90 342.89 310.62 302.30

これは各条件1回の計測で記録した範囲であり、destroyに関する列間差は小さい範囲に収まっています。destroyはdeploy時の--express--no-waitなどを受け継がないため、列間差をdeployモード由来とは判断しません。

シナリオ: baseline

baselineのdestroyは、CDKが72.07〜73.86秒(列間差1.79秒)、cdkdが9.80〜11.19秒(1.39秒)でした。

シナリオ: serverless

serverlessはCDKが88.21〜95.92秒(7.71秒)、cdkdが10.08〜11.81秒(1.73秒)でした。

シナリオ: wide

wideのdestroyはCDKが73.80〜74.21秒、cdkdが10.08〜13.50秒でした。

シナリオ: async

asyncではCDK defaultが1269.91秒、CDK expressが204.90秒、cdkd full、cdkd default、cdkd no-waitが342.89秒、310.62秒、302.30秒でした。

asyncの早期終了トラックだけを見るとCDK expressの204.90秒がcdkd no-waitの302.30秒より短く、作成時の傾向とは異なります。asyncの削除確認までの値は、CLI終了時間と分けて次に示します。

asyncの完了時間と削除時間

asyncでは、deployのCLI終了、利用可能まで、destroyのCLI終了、削除確認までを別々の指標として記録しました。

「利用可能まで」はdeploy開始から、サービス別の確認に成功し、確認用ファイルの削除と計測状態の保存を終えるまでの時間です。「削除確認まで」はdestroy開始から、destroyのCLI終了後にサービス別APIで検査可能なリソースの消失を確認し、計測状態を保存するまでの時間です。

モード deployのCLI終了 利用可能まで destroyのCLI終了 削除確認まで
CDK default 312.89 400.87 1269.91 1283.91
CDK express 296.47 383.03 204.90 219.21
cdkd full 161.51 251.75 342.89 356.72
cdkd default 134.55 259.57 310.62 324.86
cdkd no-wait 21.58 225.45 302.30 318.50

deployのCLI終了

deployのCLI終了では、cdkd no-waitが21.58秒でCLI終了しました。destroyのCLI終了では、CDK expressが204.90秒、cdkd no-waitが302.30秒でした。

いずれもCLIプロセスが終了するまでの時間であり、後続の削除確認を含みません。

利用可能まで

cdkd no-waitの「利用可能まで」は225.45秒でした。21.58秒は作成要求を出してCLIが戻るまで、225.45秒はdeploy開始から利用可能までの時間であり、同じ完了時点を表していません。

cdkd fullとcdkd defaultの「利用可能まで」は251.75秒と259.57秒、CDK defaultとCDK expressは400.87秒と383.03秒でした。

削除確認まで

サービス別APIによる削除確認まで含めると、CDK expressは219.21秒、cdkd no-waitは318.50秒でした。destroyのCLI終了時間と削除確認までの時間は異なるため、別指標として読みます。各条件1回の結果であり、作成時と削除時の順序を一般化はできません。

まとめ

デプロイではcdkdコマンドがどれだけ速いかが確認できました

4つのシナリオすべてで、同じような待ち方をするモード同士を比べると、cdkdの作成コマンドがCDKより早く終了しました。

特にasyncで、リソースの安定化を待たずにコマンドを終了するモードを比べると、CDK expressは296.47秒、cdkd no-waitは21.58秒でした。ただし、これはコマンドが終了するまでの時間です。cdkd no-waitでリソースが利用可能になるまでには225.45秒かかっているため、「21.58秒でデプロイが完了した」という意味ではありません。

一方、同じasyncの削除コマンドは、CDK expressが204.90秒、cdkd no-waitが302.30秒でした。作成コマンドが早かった方法でも、削除が早いとは限らない結果になりました。

待ち時間の違いだけが理由とは断定できません

cdkd no-waitが早く終了した理由の一つは、リソースの安定化を待たないことです。しかし、CDKとcdkdの両方で利用可能状態まで待つモードや、それぞれの既定モードを比べた場合もcdkdのほうが早く終了しました。そのため、今回の差を「待ち時間を省略したから」とだけ説明することはできません。

CDKは、リソース操作をCloudFormationへ依頼し、スタック全体の進行状況を確認します。cdkdはCloudFormationスタックを作らず、AWSの各サービスへ直接リクエストします。この処理経路の違いや、変更内容の確認、イベントの監視、リソースが安定するまでの待ち方などが、時間差に関係した可能性があります。

ただし、今回は各条件を1回ずつしか計測していません。どの処理が何秒の差を生んだのかまでは特定できません。asyncの削除時間についても、削除するリソースの種類や依存関係、AWS内部の混雑、ネットワークなど、複数の要因が考えられます。

CDKとcdkdをどう使い分けるか

開発・テスト環境を何度も作り直し、コマンドを待つ時間を短くしたい場合は、cdkdが選択肢になります。特に--no-waitを使う場合は、コマンドが終了した直後にテストを始めず、必要なリソースが利用可能になったことを別途確認する必要があります。

一方、本番環境などで変更内容の事前確認、失敗時のロールバック、構成のずれの検出、変更履歴、組織的なレビューを重視する場合は、CloudFormationを利用するCDKが候補になります。

単純に速いほうを選ぶのではなく、必要な安全性、障害からの復旧方法、監査、運用手順を含めて選ぶことが重要です。

ステージング環境デプロイのタイミングではcdkdではなくてCDKでデプロイして、本番環境デプロイでも問題なくデプロイができるよねっていうことを担保しておきたいですね。
そういう点では、開発環境, テスト環境, サンドボックス環境のような、本番・ステージング環境とは切り離された環境でcdkdを使っていくのが現状の最善策なのではないかなぁというのが私の意見です。

最後に注意書き

  • 各条件の計測は1回だけです。同じ条件でも、実行する日時によって結果が変わる可能性があります。
  • 計測時のAWS側の混雑、インターネット接続、Lambdaの起動時間、APIの利用制限などは同じ条件にそろえていません。
  • CDKとcdkdでは、「コマンドを終了してよい」と判断する条件が完全には同じではありません。特にcdkd no-waitは、リソースの安定化を待たずに終了します。
  • 今回比較したのは時間だけです。CloudFormationが提供するロールバック、変更内容の事前確認、構成のずれの検出、変更履歴などの価値は数値に含めていません。
  • この結果は、このリポジトリの構成を東京リージョンで計測した時点のものです。ほかの構成やリージョンでも同じ結果になるとは限りません。

より確かな比較にするには、同じ条件で少なくとも5回計測し、中央値や結果のばらつきを確認する必要があります。さらに計測回数を増やす場合は、大半の実行がどの時間に収まるかを示すp95も参考になります。また、AWS APIを呼び出した回数やCloudFormationのイベント数を記録すると、どの処理に時間がかかったのかを調べやすくなります。

計測した証跡

以下のGithubリポジトリでソースを公開しています。

以上!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?