コードレビューは主観的または客観的(定量化可能)ですか?


55

コードレビューのガイドラインをまとめています。正式なプロセスはまだありませんので、正式にしようとしています。そして、私たちのチームは地理的に分散しています。

私たちが使用しているTFSをソース管理のために(私たちは、同様のタスク/バグトラッキング/プロジェクト管理のためにそれを使用するが、我々は、移行とJIRA)開発のためのVisual Studio 2008で。

コードレビューを行う際にあなたが探しているものは何ですか?

  • これらは私が思いついたものです
    1. 強制FxCopののルールを(我々は、Microsoftの店です)
    2. パフォーマンス(任意のツール?)とセキュリティ(OWASP-コードクローラーの使用について考えています)およびスレッドセーフを確認します。
    3. 命名規則に従う
    4. コードはエッジケースと境界条件をカバーする必要があります
    5. 例外を正しく処理する必要があります(例外を飲み込まないでください)
    6. 機能が他の場所で複製されているかどうかを確認します
    7. メソッド本体は小さく(20〜30行)、メソッドは1つのことと1つのことのみを行う必要があります(副作用はなく、一時的な結合を回避します-)
    8. メソッドでnullを渡さない/返さない
    9. デッドコードを避ける
    10. パブリックおよび保護されたメソッド/プロパティ/変数を文書化する

他に何か気をつけるべきことはありますか?

レビュープロセスを定量化できるかどうかを確認しようとしています(別の人がレビューすると同じ出力が生成されます)。例:「メソッド本体は、体は小さくなければなりません」。

または、コードレビューは非常に主観的ですか(また、レビュー担当者ごとに異なります)。

目的は、開発者がコードをチェックインする際により慎重になるように、マーキングシステム(各FxCopルール違反ごとに-1ポイント、命名規則に従わない場合-2ポイント、リファクタリング用に2ポイントなど)を持つことです。これにより、常に良い/悪いコードを書いている開発者を特定できます。目標は、レビュー担当者がレビューを行うために最大で約30分を費やすことです(変更セット/リビジョンに複数のファイル/既存のアーキテクチャへの大きな変更などが含まれる可能性があることを考えると、これは主観的なことですが、一般的な考え方として、レビュー担当者は誰かのコードをレビューするのに何日も費やすべきではありません)。

開発者が書いた良い/悪いコードを識別するために、他にどのような客観的/定量化可能なシステムに従っていますか?

ブックリファレンス:Clean Code:Robert Martinによるアジャイルソフトウェアの職人技のハンドブック


8
nullを返す際に有害と考えられるものは何ですか?NULLの代わりに空の配列を返す方が、C#などの高水準言語で通常より良い理由を理解しています(コードをよりエレガントでエラーを回避しやすくします)。ただし、NULL参照を返す必要がある場合がありますよね?

4
nullを返さないようにすれば、クライアント/消費アプリ/ライブラリがメソッドを呼び出すときにnullのチェックをスキップできます。ロバート・マーティンによるクリーン・コードから-第7章(エラー処理)pp:110 "nullを返すとき、私たちは本質的に自分自身のための作業を作成し、発信者に問題を強要します。コントロールの。」

3
本を購入したくない人のために1ページを読むためにそれを説明できますか?...今度はより多くのエラーのためのレシピである、ほとんどのC#のプログラムのため、NULLを回避するコードをより複雑にするために起こっているようだ

2
nullを返すことが悪い考えである理由を説明する1つのブログ投稿があります。thehackerchickblog.com/2008/10/…。そしてもう1つleedumond.com/blog/should-we-return-null-from-our-methodsです。彼の本でボブが示唆しているのは、nullを返したい場合、Null参照例外をスローするか、代わりにSPECIAL_CASEオブジェクトを返すことができるということです。メソッド呼び出しの連鎖を考えてみてthis.Foo().FooBar().FooBarBar(); ()を呼び出すときにFooBarのオブジェクトはfooがnullであるから、あなたは間違いなく「オブジェクト参照がオブジェクトのインスタンスに設定されていません」を避けることができ、ここで返された場合

@SoloBold:指摘するだけで、それらは単なるガイドラインです。(いくつかのケースであってもよい)はnullを返すために非常に説得力のある理由がある場合、帰国nullはSPECIAL_CASEオブジェクトを返すよりも理にかなって

回答:


25

レビューで個人採点することは、私が取り組んできたほとんどの成功したシステム、おそらくすべてに反するものです。しかし、私が20年以上にわたって達成しようとしてきた目標は、バグの減少とエンジニア1時間あたりの生産性の向上です。個人の採点が目標であれば、レビューを使用できると思います。労働者として、あるいはリーダーとして、それが必要とされる状況を見たことはありません。

客観的な研究(ファーガンなど)と多くの一般的な知恵は、ピア関係がバグの削減と生産性の向上を目的としたコードレビューを促進することを示唆しています。働くマネージャーは、マネージャーとしてではなく、ワーカーとして参加できます。議論のポイントが記載されています。レビューアを満足させるための変更は一般に良いことですが、必須ではありません。したがって、ピア関係。

C、C ++、Javaのlintを使用してさらに分析や判断を行わなくて受け入れられる自動化ツールはすべて適切です。定期的なコンパイル。コンパイラーはコンパイラーのバグを見つけるのが得意です。自動チェックの逸脱を文書化することは、自動チェックの微妙な告発のように聞こえます。逸脱を許可するコードディレクティブ(Javaと同様)は、かなり危険です。デバッグに最適で、問題の核心をすばやく取得できます。不十分に文書化された50,000行の非コメント行のコードブロックで、それを見つけるのはあまり良くありません。

いくつかのルールは愚かですが、施行するのは簡単です。たとえば、到達不能な場合でも、すべてのswitchステートメントのデフォルト。それは単なるチェックボックスであり、何にも一致しない値で時間とお金をテストする必要はありません。ルールがあれば、愚かさがあるでしょう、それらは密接にリンクされています。ルールのメリットは、その愚かさの価値があるはずであり、その関係は定期的にチェックする必要があります。

一方、「それが実行される」ことは、レビューまたはレビューの防御の前には美徳ではありません。開発がウォーターフォールモデルに準拠している場合、複雑なエラーが発見され解決される前に、コーディングが85%完了したときにレビューを行いたいと思います。実際の生活はウォーターフォールモデルではないため、いつレビューするかはやや芸術的であり、社会的規範になります。あなたのコードを実際に読んで、問題を探す人は金色です。これを継続的にサポートしている経営陣は、価格以上の真珠です。レビューは、チェックインのように早く、頻繁に行う必要があります。

私はこれらのことが有益だと感じました:

1)スタイル戦争なし。開いた中括弧は、指定されたファイルの整合性チェックのみの対象となります。すべて同じです。それで結構です。同じインデントの深さ** sと**タブ幅。ほとんどの組織は、大きなスペースとして使用されるタブの共通標準が必要であることを発見しています。

2) `不規則

   looking

しないテキスト

   line up is hard to read 

コンテンツ用。

ところで、K&Rは 5つの(5)スペースをインデントしたので、権威への訴えは無価値です。ただ一貫してください。

3)レビューするファイルの行番号が変更されていない、公開されているコピーは、レビューの72時間以上前にポイントする必要があります。

4)オンザフライのデザインはありません。問題がある場合、または問題がある場合は、その場所を書き留めて移動し続けます。

5)開発環境のすべてのパスを通過するテストは、非常に良いアイデアです。大規模な外部データ、ハードウェアリソース、顧客のサイトの使用などを必要とするテストは、莫大な費用がかかるテストであり、完全ではありません。

6)作成、表示、編集などのツールが存在する場合、または開発の初期段階で作成される場合は、非ASCIIファイル形式を使用できます。これは私の個人的な偏見ですが、1ギガバイト未満のRAMで支配的なOSが独自の方法で抜け出せない世界では、なぜ10メガバイト未満のファイルが何であるべきかを理解できませんASCIIまたは他の商業的にサポートされている形式以外。グラフィックス、サウンド、ムービー、実行可能ファイル、およびそれらに付属するツールには標準があります。いくつかのオブジェクトのバイナリ表現を含むファイルには言い訳はありません。

リリースされたコードのメンテナンス、リファクタリング、または開発のために、ブランチチェックインのゲートウェイとして、ディスプレイに座って古いものと新しいものの差分を見ながら、他の人によるレビューを使用していた同僚のグループ。私はそれが好きでした、それは安くて、速くて、比較的簡単でした。事前にコードを読んでいない人のためのウォークスルーは、開発者のコ​​ードを改善することはほとんどありませんが、すべての人にとって教育的です。

地理的に分散している場合は、同じものを見ている他の人と話しながら画面上の差分を見るのは比較的簡単です。それは、変化を見ている二人をカバーしています。問題のコードを読んだことのある大規模なグループにとって、複数のサイトは1つの部屋にあるすべてのサイトよりもそれほど難しくありません。共有のコンピューター画面とスクークボックスでリンクされた複数の部屋は非常にうまく機能します。サイトが多いほど、より多くの会議管理が必要になります。ファシリテーターとしてのマネージャーは、ここでキープを獲得できます。あなたがいないサイトをポーリングし続けることを忘れないでください。

ある時点で、同じ組織に回帰テストとして使用される自動化された単体テストがありました。本当に良かった。もちろん、その後プラットフォームを変更し、自動化されたテストは取り残されました。アジャイルマニフェストが指摘しているように、プロセスやツールよりも関係の方が重要である、レビューの方が優れています。しかし、レビューが済んだら、自動化された単体テスト/回帰テストは、優れたソフトウェアを作成する上で次に重要な助けになります。

女性が「ハリー・メット・サリーのとき」で言うように要件に基づいてテストを行うことができれば、彼女が持っているものを手に入れることができます!

すべてのレビューにはコーディングを超えるレベルで要件と設計の問題を把握するための駐車場が必要です。何かが駐車場に属していると認識されたら、レビューで議論を停止する必要があります。

コードレビューは、ハードウェア設計の回路図レビューのようにすべきだと思うことがあります。完全に公開され、徹底的で、チュートリアル、プロセスの終わり、ゲートウェイが構築およびテストされます。しかし、物理オブジェクトの変更には費用がかかるため、スケマティックレビューは重量級です。ソフトウェアのアーキテクチャ、インターフェイス、およびドキュメントのレビューは、おそらく重いはずです。コードはより流動的です。コードレビューはより軽量でなければなりません。

多くの点で、テクノロジーは特定のツールに関するものと同じくらい文化と期待に関するものだと思います。心を楽しませ、心に挑戦する「スイスファミリーロビンソン」/ フリントストーン / マクガイバーの即興演奏を考えてみてください。私たちは自分のものを機能させたい。それへの単一の道はありません。1960年代のAIプログラムによって何らかの形で抽象化および自動化できる「インテリジェンス」があった以上です。


特に人の評価に関しては、これは良い答えです-これはコードレビューのポイントではないはずです。
水田

25

あなたが説明したポイントのほとんどは、コードのフォーマットや「表面」の問題だけです。

  • 命名規則に従う
  • デッドコードを避ける
  • 資料
  • ...

これはすべて自動化されたツールを使用して確認できます。経験豊富な開発者がコードを確認して時間を費やす必要はありません。

.NETについてはまったく知りませんが、PHPには、そのようなものをチェックするツールがあります。.NETはしばしばPHPよりも「産業的」であると言われていることを考えると、そのようなことをチェックするツールがないと聞いて驚くでしょう。


自動化ツールは次の両方を実行できます。

  • 毎晩実行される自動ビルドプロセスに統合される
  • メールレポートを送信する
    • 警告(たとえば、メソッドが20行より長い)
    • エラー(たとえば、メソッドが50行より長い)

メールはすべてのチームに送信するか、テストに合格しないコードをコミットした人に送信するか、レポート用のWebインターフェイスを 使用できます(.NETとPHPについての同じメモ)


また、コードが本番環境で使用される前に特定の数のエラーを検出するために、自動テストが大いに役立つことも追加します。そして、自動化されたテストは、いくつかのメトリックスにも役立つと思います。


経験豊富な開発者が行った コードレビューには、あなたが話さなかった別の大きな利点もあります。

  • 経験豊富な開発者は、ソースコードを調べるだけで、さまざまなバグを検出できることがよくあります(コードレビューを行うと、バグを見つけることがよくあります)。
  • 経験豊富な開発者が行ったコードレビューにより、チームにコメントや推奨事項を作成できます
    • 彼はコードで使用されているアルゴリズムを理解しようとし、おそらくより良い解決策を提案します。
    • コードを読むだけで、多くの場合、自動化ツールでは検出されないことがわかります。

しかし、コードの書式設定よりも深いコードレビューを行うには、30分以上必要です...


.Net用のこのツール(C#のみ)はStyleCopです。code.msdn.microsoft.com/sourceanalysis
ブライアンアンダーソン

15

コードレビューの私の経験は、コードを改善するための総合的な努力であるべきであり、彼らの仕事で良い人か悪い人かを決定する「尺度」ではないということです。コードのレビュー中に多くのコメントを受け取っても問題ない場合、レビュー担当者はより厳格になるため、コードを改善するための提案を行います。

チェックインコードの品質を向上させるには、レビューコメントが処理されるようにし(レビューアに処理済みのコメントを承認させます)、静的コードチェックツールを使用して初期コミットの品質レベルを強制します。


2
これを仕事で優れている人の比較にしないことについてのあなたのコメントに対して+1。これは士気に悪いでしょう!

2
@KarstenF:はい。また、DeveloperAはより複雑なタスク(より多くのコード行)で作業しているのに対して、DeveloperBは単純なタスクで作業しており、スコアが少ない(ポイントスケールで)場合があります。DevAがジョブ/タスクの両方を正規化する方法がないときに悪い仕事をしたと言うのは不公平だと思います

2
また、一部の開発者は同僚の信用を傷つけようとするかもしれません。

この点はまさに正しい。ささいな概念(グレーディングなど)は、ペティネスにつながります。
ダンローゼンスターク

この非常に重要な点で+1。あなたのプロセスが数字の生産を開始するとすぐに、人々は自分のコードをゲームして数字を増やします。たとえば、彼らはペナルティ/メソッドの評価が非常に低いように、単純なコードの多くの行を書きます。または、彼らはすべての時間を費やして完全な変数名を見つけます。そして、それは政治的なものになります。なぜなら、それは彼らのスコアを下げ、それらを悪く見せるので、誰も彼らの友人のコードの小さな誤りを指摘したくないからです!やばい!要するに、あなたの心は正しい場所にありますが、悪い考えです。プログラマーは犬を見せません。
レオガー

5

あなたの評価システムは悪い考えだと思います。ポイントは?良いプログラマーと悪いプログラマーを識別するには?そのコードレビューの誰もが、コードレビューで提示されたコードに基づいて、特定のプログラマーに関する評価を、やや任意の特性セットへの値の任意の割り当てよりもよく行うことができます。良いプログラマーと悪いプログラマーを特定したい場合...プログラマーに尋ねてください。私は、人間があなたの愚かな発見的方法よりもこの評価をよりうまく行えるこ​​とを保証します。

私の提案は、コードレビューを改善して、人々が非判断的で非敵対的な環境でアイデアや意見をオープンに共有できるようにすることです。もしそれができれば、プログラマーを評価する良い仕事をすることを意図した愚かなチェックリストに基づいてプログラマーに判断を下すよりも100倍良いでしょう。多くのプログラマーは、コードレビューが不十分な場合、すでに誇りに思っており、自分自身に苦労していると思います。パフォーマンスの低下に対するさらなる「罰」が一般に役立つかどうか疑問です。


4

私の唯一のアドバイスは、コードレビュープロセスを厳しすぎることを避けることです。最も重要なことは、コードレビューが実際に行われ、真剣に受け止められることです

レビューアにとってプロセスが使い果たされるほど、コードレビューが発生する可能性は低くなり、単に煩わしいと見なされるのではなく、真剣に受け止められるようになります。また、コードレビューの真価は、 FXCopルールが合格するかどうかなどを確認するために、レビュアーが独自の判断自動化ツールを使用できることです。


+100!つまり、+ 1ですが、実際のところ、これがポイントではありません。コードレビューと単体テスト(およびその他のもの)については、少ないほうが重要です。これは、ゼロになるまでより多くの情報が得られるためです。)
ダンローゼンスターク

4

経験則として、コードレビューに時間を費やしてマシンで実行できることを行わないでください。たとえば、最初の項目は「FxCopルールを強制する」ことですが、おそらくFxCopによって、人間がそれを行う必要なく行うことができます。


3

それを測定できる場合、それが客観的で定量化できる場合は、ツールにそれを試してもらいます。経験豊富なレビュアーが必要なのは、曖昧な主観的なものです。


ツールを作成するのに100時間、それを使用して1000が保存されました。
ダンローゼンスターク

3

スタイルの問題については、すでに多くの良いコメントが出されていますが、これは重要です。チームプロジェクトでは、すべてのコードが1人の作成者によって作成されたように見えることが重要です。これにより、チームの他のメンバーが、問題が発生したときに立ち寄り、問題を修正しやすくなります。このより広い目標を確保するために選択する定量的尺度は、それほど重要ではありません。

もう1つの項目は、システムの残りの部分について合意された全体的なアーキテクチャとコードが一致することを保証することです。同様の問題はすべて同じ方法で解決する必要があります。アプリケーションロジックが複数のレイヤーに分割されている場合、レビュー対象のコードは、システムの他の部分と同じように機能を分割しますか?あるいは、レビュー中のコードは、システムの残りの部分に引き戻されるべき何か新しいことを教えていますか?スタイルチェックでコードがすべて同じに見えることを確認するように、アーキテクチャレビューではコードがすべて同じように機能することを確認する必要があります。ここでも、保守性に重点が置かれています。チームの誰もがこのコードに立ち入り、何が起きているのかをすぐに把握できるはずです。

グレーディングのアイデアは、制作の惨事のように思えますが、チームを最もよく知っています。彼らがそのようなシステムに動機付けられる可能性はありますが、問題を解決するよりも人々が自分の成績について心配し始める可能性が高いと思います。コードレビューの本当に価値のある副作用の1つは、彼らが提供するメンタリングの機会です。レビューアは、コードを書いた人をメンターしている人として扱うべきです。見つかった各問題は問題ではありませんが、より知識が豊富で洗練されたチームメンバーを作成し、チーム全体をより緊密に編成する機会です。


2

率直に言って、私は実際に「主観的」なものよりも何よりも気にしています。良いコードレビューで欲しいのは、入力ではなく、誰かが私のロジックをチェックすることです。そして、コードレビューを行う際に私が注力していることです。

私が採用したい一般的な形式は次のとおりです。

  1. 何を修正しますか?
  2. 何が原因でしたか?(コードを見てください)
  3. どのように修正しますか?
  4. 新しいコードを見せて
  5. 動作するコードを見せてください

それなしでは、差分を見るだけで、軽微な問題や文体的な点に関する情報を提供する傾向があります。私は、ロジックが正しいかどうか、全体的に使用されるアプローチが良いかどうか、そしてソリューションが維持可能かどうかにはるかに関心を持っています。

例として、私は最近、同僚によるコードを見ました。元の問題はFxCop違反でした。しかし、実行されていたのは、バージョン番号をチェックすることにより、Windows機能の有無を判断しようとしていました。私の主な意見は、これはそうするのに脆弱な方法であり、機能の存在とWindows skuの間のマッピングは将来変更される可能性があり、まったく将来性がないため、サービスに直接クエリする方が良いでしょう。


あなたの答えからは明らかではありません:FxCopはその脆弱性を捕らえましたか?
ダンローゼンスターク

2

循環的複雑度(CC)は、「悪くない」コードを評価する1つの方法です。

CCが高い実際のコードでは、「ここで何が起こっているのか、覚えていない」要素が高くなっています。低いCCコードの方がわかりやすいです。

明らかに、メトリックには通常の警告が適用されます。


1
@AfermeraInfo:ハァッ?
ポールネイサン

1

コードレビューは主観的かつ客観的です。「メソッド本体は20〜30行である必要があります」などのルールは主観的です(一部の人は100行でよいと考えるかもしれません)が、20〜30行が制限であると会社が決定した場合、それで問題ありません。あなたが思いついたポイントシステムは素晴らしいアイデアだと思います。特定のルールはスコアリングに多少の重みを付ける必要があることがわかったため、定期的に再評価する必要がありますが、誰もがルールを知っている限り、それは良いシステムのように見えます。

私が探している他のもの:

  • コードの明確さ-コードが1行または複数行で記述される場合があります。平均的なプログラマーは、コード行が何をするのかを理解しようとして数分を費やす必要はありません。もしそうなら、コードはもっと簡単な方法で書き直されるべきでしょう。これは主観的ですが、重要な点は、会社のほとんどのプログラマーがコードをすぐに理解できるようにすることです。
  • 関数の入力パラメーターを確認します-入力パラメーターが許容範囲内に収まることを確認するコードが必要です。これは、機能のドキュメントとも一致する必要があります。
  • 記述的な変数名-いくつかの特別な場合(ループインデックスなど)を除き、変数名は記述的なものでなければなりません。命名規則などを確認したいリソースの1つは、コードの完成です

1

詳細が速すぎます。もっと分解する必要があります。コードの品質と機能のコンプライアンスを確認する必要があります。両方を分離する必要がありますが、それで話は終わりではありません...

コード品質:

  • 自動チェック:
    • スタイルの構成:命名規則が正しいか、すべてのコードが適切にインデントされているかなど。
    • 効率基準:メモリリークのチェック、複雑さのチェック、冗長変数など
  • 実際のピアレビュー:
    • 設計の簡単な説明
    • 自動チェックからの逸脱の説明
    • メンテナンスの容易さ、メンテナンス方法とすべてについて話し合う
    • テスト容易性:このコードのテストはどれくらい簡単ですか?計画を立てましたか?

機能コンプライアンス:

  1. 機能要件のレビューと、要件および/または設計レビュー以降の変更
  2. 要件に関連する機能をデモし、それらを1つずつ確認します
  3. 実装中に遭遇するソフトウェアの他の側面(展開計画、インフラストラクチャなど)の追加要件について話し合う
  4. 要件からの逸脱の説明(ある場合)。

コードレビューのこれらの2つの側面について自分自身をカバーできるなら、あなたは最高です。



1

場合によります。

レビューの一部は簡単に定量化できます(FxCopの問題、StyleCopエラー、CAT.NETエラーなどはありません)

あなたはより具体的であること始めると、あなたはそれを測定することができ、などのツール(ノー方法> 20行)、しかし、あなたが言うよう-スタイルは、しかし、主観的NDependのが自動的にそれを行うことができます。ただし、自動化されないものもあります。エッジケースの処理をチェックするにはテストが必要になり、コードカバレッジが向上します。多くの場合、100%は到達不可能な理想です。重複チェックは自動的に行うのが困難です。Nullチェック、あなたに同意するかどうかはよくわかりませんが、NDependルール、またはそのルールのFxCopルールを作成できる場合があります。

ツールが多ければ多いほど良いのです。開発者が変更をコミットする前に作業をチェックし、CIプロセスの一部としてチェックを実行できるようにすれば、レビューの必要性を最小限に抑えることができます。


0

マーキングシステムは正しく機能するのは難しいように思えますが、測定ツールとして持つ価値はあります。測定できないものを改善することはできません。ただし、正確に定量化するのが難しい/不可能なこともあることをおそらく受け入れる必要があります。トリッキーなことは、各品質が何ポイントを獲得するかを決めることです。例えば、命名規則を順守している場合、2ポイントを獲得します。

コードを特定の品質に適合、部分的に適合、または不適合としてマークできるように、単純なチェックリストのようなものが良いかもしれません。後で、どの品質問題が最も頻繁に発生するか、またはほとんどの問題を引き起こすかを確認したら、チェックリストにスコアリングを追加できます。

また、レビュープロセスは、コードがレビューの一部に失敗するのに十分な柔軟性を備えている必要があります(ただし、正当化および文書化できる場合)。コンポーネントが不必要に複雑/管理不能になるコード品質基準に盲目的に固執することは悪い考えです!


完璧なことが起こります。

0

一部の開発者が文句を言うように、「フォーマットに時間を浪費する」ことなく、人々のコードをより標準化したい場合。フォーマットやその他のリファクタリングタスクの修正をほぼ自動化されたプロセスにするため、ReSharperのようなツールに投資してください。


0
  • マシンがそれをチェックできる場合、人々はすべきではありません。
  • 1つのチェックリスト項目のみ:すべてのエラーケースはどこでも正しく処理されますか?
  • コードレビューを使用して品質を向上させ、知識を伝達します。
  • 「悪い」開発者を特定するためにコードレビューを使用しないでください。
  • 自我は、明示的なポイントよりも効果的です。
  • 短くしてください-90分、500行は巨大です。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.