A Dart Library to Generate, Checksum, and Validate Ethereum Addresses

ethereum_addresses

Usage

import "package:convert/convert.dart" show hex;
import "package:ethereum_addresses/ethereum_addresses.dart";

final publicKey = hex.decode(
  "028a8c59fa27d1e0f1643081ff80c3cf0392902acbf76ab0dc9c414b8d115b0ab3",
);

// Derives an Ethereum address from a given public key.
ethereumAddressFromPublicKey(Uint8List.fromList(hex.decode(publicKey)),);
// => "0xD11A13f484E2f2bD22d93c3C3131f61c05E876a9"

// Converts an Ethereum address to a checksummed address (EIP-55).
checksumEthereumAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed");
// => "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"

// Returns whether a given Ethereum address is valid.
isValidEthereumAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed");
// => true
isValidEthereumAddress("0x5aaeb6053F3E94C9b9A09f33669435E7Ef1beaed");
// => false

License

MIT License

Copyright (c) 2019 Peter Jihoon Kim

Use this package as a library

Depend on it

Run this command:

With Dart:

 $ dart pub add ethereum_addresses

With Flutter:

 $ flutter pub add ethereum_addresses

This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get):

dependencies:
  ethereum_addresses: ^1.0.2

Alternatively, your editor might support dart pub get or flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:ethereum_addresses/ethereum_addresses.dart'; 

example/main.dart

import 'dart:typed_data';

import "package:convert/convert.dart" show hex;
import "package:ethereum_addresses/ethereum_addresses.dart";

void main() {
  final publicKey = hex.decode(
    "028a8c59fa27d1e0f1643081ff80c3cf0392902acbf76ab0dc9c414b8d115b0ab3",
  );

  // Derives an Ethereum address from a given public key.
  print(ethereumAddressFromPublicKey(Uint8List.fromList(publicKey)));

  // Converts an Ethereum address to a checksummed address (EIP-55).
  print(checksumEthereumAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"));

  // Returns whether a given Ethereum address is valid.
  print(isValidEthereumAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"));
  print(isValidEthereumAddress("0x5aaeb6053F3E94C9b9A09f33669435E7Ef1beaed"));
} 

Download Details:

Author: fuseio

Source Code: https://github.com/fuseio/dart-ethereum_address

#darts #code 

A Dart Library to Generate, Checksum, and Validate Ethereum Addresses
Mike  Kozey

Mike Kozey

1656822600

Bare_codegen: Code Generator to Covert BARE Schema To Dart Classes

BARE Code generator

The code generator transforms all *.bare files into

  • *.dart
  • *.bare.dart

It produces the necessary classes and the extension methods.

After running the code generator with

  $ dart run build_runner build

To encode a class

<class_name>.toBare

To decode to a class

<class_name>.fromBare(bytes)

See the example folder for a sample of a schema file and the generated code.

Code generator config options:

to_string - Generate toString override for the generated classes. Default - True

targets:
  $default:
    builders:
      bare_codegen|bareGenerator:
        options:
          to_string: True

To run the project:

Update your dependencies:

$ cd bare && pub get
$ cd bare_codegen && pub get
$ cd example && pub get

cd into example and run build process and run tests:

$ dart run build_runner build
$ dart test

Installing

Use this package as a library

Depend on it

Run this command:

With Dart:

 $ dart pub add bare_codegen

With Flutter:

 $ flutter pub add bare_codegen

This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get):

dependencies:
  bare_codegen: ^0.1.2

Alternatively, your editor might support dart pub get or flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:bare_codegen/bare_codegen.dart';

Author: Kaashyapan
Source Code: https://github.com/kaashyapan/bare-dart 
License: MIT

#flutter #dart #code 

Bare_codegen: Code Generator to Covert BARE Schema To Dart Classes
Saul  Alaniz

Saul Alaniz

1655811120

¿Puede Resolver Los Conflictos De Combinación Antes De Que Sucedan?

¡Resolver conflictos de fusión es divertido!  - nunca dijo nadie. A nadie le gustan, pero eso no cambia el hecho de que son, bueno, parte del trabajo. En esta breve publicación, me gustaría explicar brevemente de dónde vienen, mostrar cómo resolverlos y, como beneficio adicional, ¡sugerir una solución para evitarlos potencialmente en primer lugar!

Fusionar conflictos: ¿qué son?

Un conflicto de combinación es un evento que ocurre cuando Git no puede resolver automáticamente las diferencias en el código entre dos confirmaciones. Pueden ocurrir como resultado de la fusión de ramas, durante una reorganización o cuando estás seleccionando en Git.

La buena noticia es que Git es bastante bueno para descifrar cómo integrar cambios, por lo que la mayor parte del tiempo está a salvo. La parte difícil comienza cuando se modifica exactamente la misma línea y Git no puede decidir qué versión es la correcta. Te lo notificará en la terminal y te hará responsable de arreglarlo.

Resolver conflictos de combinación de la forma habitual

Bien, entonces, ¿cómo resolvemos los conflictos? Podemos resolverlos manualmente o usar una GUI de Git como las integradas en VS Code o una aplicación de escritorio separada como  Tower  o  Sourcetree .

Veamos cómo resolver conflictos manualmente.

Los cambios en conflicto son fáciles de detectar, ya que se marcarán en un archivo con  <<<<<<< y  >>>>>>>. La parte anterior  ======= es su versión de las líneas en conflicto y la parte posterior es su versión. Elimine las líneas que no desee (incluidos los marcadores de conflicto) y conserve las que sean correctas.

Detección temprana de conflictos de fusión

Los conflictos de fusión no dan tanto miedo como parecen, pero la verdad es que resolverlos puede ser un proceso largo y tedioso, especialmente en proyectos grandes.

Ahí es donde GitLive es útil: una poderosa herramienta que le avisará dentro de su IDE en el momento en que realice un cambio en el editor que entre en conflicto con un cambio en cualquier otra rama. Las advertencias le muestran conflictos potenciales y le brindan la oportunidad de resolverlos antes de que ocurra el conflicto de combinación.

Comenzando con GitLive

Instale la última extensión de GitLive  VS Code  para comenzar.

Cuando abre un archivo, verá los indicadores de cambio en el margen de su editor que le muestra dónde sus compañeros de equipo han realizado cambios en comparación con su versión del archivo. Estos se actualizan en tiempo real a medida que usted y sus compañeros de equipo están editando.

Si ha realizado un cambio conflictivo, verá el indicador de conflicto de color rojo brillante. Estos conflictos pueden ser cambios locales no confirmados que aún no ha enviado o cambios existentes en su rama que entran en conflicto con los cambios de sus compañeros de equipo.

Pase el cursor sobre las líneas afectadas en VS Code para inspeccionar un cambio y ver la diferencia, de qué rama son e incluso seleccionar cambios directamente en su archivo local.

Eso es todo, espero que encuentre útil mi miniguía sobre cómo lidiar con los conflictos de fusión y la próxima vez que encuentre un conflicto de fusión no entre en pánico. ¡Déjame saber lo que piensas en los comentarios! 

 Esta historia se publicó originalmente en https://www.c-sharpcorner.com/article/can-you-resolve-merge-conflicts-before-they-happen/

#firebase #code #vscode 

¿Puede Resolver Los Conflictos De Combinación Antes De Que Sucedan?

マージの競合が発生する前に解決できますか?

マージの競合を解決するのは楽しいです! -誰も言ったことがない。誰もそれらを好きではありませんが、それは彼らが仕事の一部であるという事実を変えることはありません。この短い投稿では、それらがどこから来たのかを簡単に説明し、それらを解決する方法を示し、追加のボーナスとして、そもそもそれらを回避する可能性のある解決策を提案したいと思います!

競合をマージします-それらは何ですか?

マージの競合は、Gitが2つのコミット間のコードの違いを自動的に解決できない場合に発生するイベントです。これらは、ブランチのマージの結果として、リベース中、またはGitでチェリーピッキングをしているときに発生する可能性があります。

幸いなことに、Gitは変更を統合する方法を理解するのに非常に優れているため、ほとんどの場合、安全です。難しい部分は、まったく同じ行が変更され、Gitがどちらのバージョンが正しいかを判断できないときに始まります。それはターミナルでそれについてあなたに通知し、あなたにそれを修正する責任を負わせます。

マージの解決は通常の方法と競合します

では、どうすれば競合を解決できますか?それらを手動で解決するか、VSCodeに組み込まれているような GitGUI、またはTower や Sourcetreeなどの別のデスクトップアプリケーションを使用することができます。

競合を手動で解決する方法を見てみましょう。

<<<<<<< 競合する変更は、ファイル内でと によってマークされるため、簡単に見つけることができます >>>>>>>。前の部分 ======= は競合する行のバージョンであり、後の部分はそれらのバージョンです。不要な行(競合マーカーを含む)を削除し、正しい行を保持します。

マージの競合の早期検出

競合をマージすることは、見た目ほど恐ろしいことではありませんが、真実は、特に大規模なプロジェクトでは、競合を解決するのに長くて退屈なプロセスになる可能性があるということです。

そこでGitLiveが便利になります。これは、他のブランチの変更と競合するエディターで変更を加えたときにIDE内で警告する強力なツールです。警告は、潜在的な競合を示し、マージの競合が発生する前にそれらを解決する機会を提供します。

GitLiveから

開始するには、最新のGitLiveVSCode 拡張機能をインストールして ください。

ファイルを開くと、エディターのガターに変更インジケーターが表示され、ファイルのバージョンと比較してチームメートが変更を加えた場所が示されます。これらは、あなたとあなたのチームメートが編集しているときにリアルタイムで更新されます。

競合する変更を行った場合は、明るい赤色の競合インジケーターが表示されます。これらの競合は、まだプッシュしていないコミットされていないローカルの変更、またはチームメートの変更と競合するブランチ上の既存の変更である可能性があります。

VS Codeで影響を受けた行をロールオーバーして、変更を調べ、差分、それらがどのブランチからのものであるか、さらにはチェリーピックの変更をローカルファイルに直接確認します。

それだけです。マージの競合に対処する方法についての私のミニガイドが役立つことを願っています。次にマージの競合が発生したときに、慌てる必要はありません。コメントであなたの考えを教えてください! 

 このストーリーは、もともとhttps://www.c-sharpcorner.com/article/can-you-resolve-merge-conflicts-before-they-happen/で公開されました。

#firebase #code #vscode 

マージの競合が発生する前に解決できますか?

Reactコードベースのバグをどのように削減したか

最近、大規模なReactアプリのコードベースを使用しているときに、コンパイル時または実行時のエラーではなく、予期しないコードの動作である3つのカテゴリのバグに対して急停止しました。

  • コンポーネントは、ユーザーイベント時に更新されません。
  • コンポーネントは、ユーザーイベント時に部分的に更新されます。
  • コンポーネントが予期せずレンダリングされます。

もちろん、私たちの最初の本能は、「悪を見つけることができるところならどこでも悪と戦うこと」でした。

しかし、一連の印刷ステートメントの後でも、バグを追跡するのは困難なままでした。そのとき、コードの特定の部分がアンチパターンと見なされる可能性があることに気づきました。

そのため、将来これらの間違いを確実に回避するために、それらを理解し、そのように特徴づけることに多くの時間を費やしました。

この記事はそれらの発見を説明する試みです。

Reactのパターンとアンチパターン

この記事では、Reactコードは、次の場合に適切なパターンと見なされます。

  • コンポーネントは再利用可能です。
  • コードのレビューとデバッグが簡単です。

上記の目的を達成するために、より多くのコード行を記述した場合、または(予想どおり)いくつかの追加のレンダリングを導入した場合でも、コードはパターンと見なされることに注意してください。

経験豊富な開発者でさえ、アンチパターンの罠に陥るのはなぜですか?

  1. Reactコードは、パターンに従う場合とアンチパターンに従う場合で驚くほど似ています。
  2. パターンは非常に明白であるため、無視されます。

アンチパターンを識別する方法は?

ヒント#1:依存関係配列のないフック

Reactでは、さまざまなコードが依存関係によって相互にリンクされています。これらの相互依存するコードは、アプリケーションの状態を目的の形式に保ちます。したがって、依存関係のないコードをReactで作成すると、バグが発生する可能性が高くなります。

したがって、などのフックを使用する場合は、依存関係の配列を使用しないため、注意が必要useStateですuseRef

別の人に怒鳴る男。 キャプション:コードを開くと、何が見つかりますか? このフックには依存関係の配列はありません!

ソース:imgflip.com

ヒント2:構成をネストする

Reactコンポーネントを配置するメカニズムは2つあります。

  1. 構成:すべての子供は同じデータを持っています
  2. ネスティング:各子は異なるデータを持つことができます

著者による画像

「Child3」にバグがあることがわかったシナリオを想像してみましょう。

コンポジションを使用してコンポーネントを配置した場合、すべてが独立しているため、「子1」と「子2」のコードを調べる必要はありません。したがって、デバッグの時間計算量はになりますO (1)

ただし、ネストを使用してコンポーネントを配置した場合は、「子3」の前にすべての子をチェックして、バグの原因を特定する必要があります。この場合、デバッグの時間計算量は、「子3」を超える子の数はO (n)どこになりますか。n

したがって、ネストすると、構成よりもデバッグプロセスが困難になることが多いと結論付けることができます。

サンプルアプリ

それでは、さまざまなパターンとアンチパターンを示すアプリについて考えてみましょう。

アプリの望ましい動作

左側のナビゲーションメニューで記事をクリックすると、右側に開きます。これに続いて2つのアクションがあります。

  1. 計算:記事の合計文字数が計算され(num_chars(title) + num_chars(text)、表示されます。
  2. ネットワークリクエスト:記事の合計文字数に基づいて、ネットワークリクエストを介して絵文字が取得され、表示されます。文字数が増えると、絵文字は悲しいものから幸せなものに変わります。

アプリの構築

このアプリを構築する正しい方法は、次の4つのステップで説明します。

  • 間違った例:アプリが期待どおりに機能しません—新しい記事が選択されたときに計算もネットワークリクエストもトリガーされません。
  • 部分的に正しい:アプリは期待どおりに動作しますが、新しい記事を選択するとちらつき効果が見られます。
  • 正しいが最適ではない:アプリはDOMのちらつきなしで期待どおりに動作しますが、不要なネットワーク要求を行います。
  • 正確で最適:アプリは、DOMのちらつきや不要なネットワーク要求なしで期待どおりに動作します。

コード構造

右下のボタンをクリックすると、上のサンドボックスを開くことができます。

ディレクトリには、src/pages各ステップにマップされたページがあります。の各ページのファイルには、コンポーネントがsrc/pages含まれています。ArticleContent議論中のコードはこのArticleContentコンポーネントの中にあります。

ここで、上記の4つのアプローチで採用されたアンチパターンとパターンを確認しましょう。

アンチパターン#1:初期状態としての小道具またはコンテキスト

間違ったアプローチでは、propsまたはcontextの初期値として使用されていuseStateますuseRef。27行目では、平均の長さが計算され、状態として保存されていることがわかります。

import { useCallback, useEffect, useState } from "react";
import { useGetArticles } from "../hooks/useGetArticles";
import { useGetEmoji } from "../hooks/useGetEmoji";
import { Articles } from "../types";
import { Navigation } from "../components/Navigation";

const styles: { [key: string]: React.CSSProperties } = {
  container: {
    background: "#FEE2E2",
    height: "100%",
    display: "grid",
    gridTemplateColumns: "10rem auto"
  },
  content: {}
};

const ArticleContent: React.FC<{
  article: Articles["articles"]["0"];
}> = (props) => {
  // Step 1. calculate length as we need it to get corresponding emotion
  const [length] = useState<number>(
    props.article.text.length + props.article.title.length
  );

  // Step 2. fetch emotion map from backend
  const emotions = useGetEmoji();

  // Step 3. set emotion once we get emotion map from backend
  const [emotion, setEmotion] = useState<string>("");
  useEffect(() => {
    if (emotions) {
      setEmotion(emotions["stickers"][length]);
    }
  }, [emotions, length]);

  return (
    <div>
      <div>
        <h2>{props.article.title}</h2>
        <div>{props.article.text}</div>
      </div>
      <h3
        dangerouslySetInnerHTML={{
          __html: `Total Length ${length} ${emotion}`
        }}
      />
    </div>
  );
};

const Incorrect: React.FC = () => {
  const articles = useGetArticles();
  const [currentArticle, setCurrentArticle] = useState<
    Articles["articles"]["0"] | null
  >();
  const onClickHandler = useCallback((article) => {
    setCurrentArticle(article);
  }, []);
  return (
    <div style={styles.container}>
      <Navigation articles={articles} onClickHandler={onClickHandler} />
      <div style={styles.content}>
        {currentArticle ? <ArticleContent article={currentArticle} /> : null}
      </div>
    </div>
  );
};

export default Incorrect;

このアンチパターンが、新しい記事が選択されたときに計算もネットワーク要求もトリガーされない理由です。

アンチパターン#2:破棄して再作成する

「DestroyandRecreate」アンチパターンを使用して、誤ったアプローチを修正しましょう。

機能コンポーネントを破棄することは、最初の関数呼び出し中に作成されたすべてのフックと状態を破棄することを意味します。再作成とは、以前に呼び出されたことがないかのように関数を再度呼び出すことを指します。

親コンポーネントは、key小道具を使用してコンポーネントを破棄し、変更するたびに再作成できることに注意してくださいkey。はい、あなたはそれを正しく読んでいます—あなたはループの外でキーを使うことができます。

具体的には、ファイル内の親コンポーネントkeyの子コンポーネントをレンダリングしながら、propを使用して「Destroyand Recreate」アンチパターンを実装します(89行目)。ArticleContentRecreateDocRecreateDoc.tsx

import { useCallback, useEffect, useState } from "react";
import { Navigation } from "../components/Navigation";
import { useGetArticles } from "../hooks/useGetArticles";
import { useGetEmoji } from "../hooks/useGetEmoji";
import { Articles } from "../types";

const styles: { [key: string]: React.CSSProperties } = {
  container: {
    background: "#FEFCE8",
    height: "100%",
    display: "grid",
    gridTemplateColumns: "10rem auto"
  },
  content: {}
};

const ArticleContent: React.FC<{
  article: Articles["articles"]["0"];
}> = (props) => {
  // Step 2. fetch emotion map from backend
  const emotions = useGetEmoji();

  // Step 3, set emotion once we get emotion map from backend
  const [emotion, setEmotion] = useState<string>("");
  useEffect(() => {
    if (emotions) {
      setEmotion(
        emotions["stickers"][
          props.article.text.length + props.article.title.length
        ]
      );
    }
  }, [emotions, props]);

  return (
    <div>
      <div>
        <h2>{props.article.title}</h2>
        <div>{props.article.text}</div>
      </div>
      <h3
        dangerouslySetInnerHTML={{
          __html: `Total Length ${
            props.article.text.length + props.article.title.length
          } ${emotion}`
        }}
      />
    </div>
  );
};

const PartiallyCorrect: React.FC = () => {
  const articles = useGetArticles();
  const [currentArticle, setCurrentArticle] = useState<
    Articles["articles"]["0"] | null
  >();
  const onClickHandler = useCallback((article) => {
    setCurrentArticle(article);
  }, []);
  return (
    <div style={styles.container}>
      <Navigation articles={articles} onClickHandler={onClickHandler} />
      <div style={styles.content}>
        {currentArticle ? <ArticleContent article={currentArticle} /> : null}
      </div>
    </div>
  );
};

export default PartiallyCorrect;

アプリは期待どおりに動作しますが、新しい記事を選択するとちらつき効果が見られます。したがって、このアンチパターンは部分的に正しい出力になります。

パターン#1:JSXの内部状態

「DestroyandRecreate」アンチパターンを使用する代わりに、この「正しいが最適ではない」アプローチでは、「rerendering」を使用します。

レンダリングとは、関数呼び出し全体でフックをそのままにして、react関数コンポーネントを再度呼び出すことを指します。「破棄して再作成」では、すべてのフックが最初に破棄されてから、最初から再作成されることに注意してください。

'rerendering'を実装useEffectするuseStateには、タンデムで使用されます。の初期値はまたはuseStateに設定でき、実行後に実際の値が計算されて割り当てられます。このパターンでは、を使用して依存関係配列の欠如を回避しています。nullundefineduseEffectuseStateuseEffect

具体的には、合計文字数の計算をJSX(50行目)に移動し、 (31行目)propsの依存関係として(41行目)を使用していることに注目してください。useEffect

import { useCallback, useEffect, useState } from "react";
import { Navigation } from "../components/Navigation";
import { useGetArticles } from "../hooks/useGetArticles";
import { useGetEmoji } from "../hooks/useGetEmoji";
import { Articles } from "../types";

const styles: { [key: string]: React.CSSProperties } = {
  container: {
    background: "#FEF2F2",
    height: "100%",
    display: "grid",
    gridTemplateColumns: "10rem auto"
  },
  content: {}
};

const ArticleContent: React.FC<{
  article: Articles["articles"]["0"];
}> = (props) => {
  // Step 1. calculate length as we need it to get corresponding emotion
  const [length] = useState<number>(
    props.article.text.length + props.article.title.length
  );

  // Step 2. fetch emotion map from backend
  const emotions = useGetEmoji();

  // Step 3. set emotion once we get emotion map from backend
  const [emotion, setEmotion] = useState<string>("");
  useEffect(() => {
    if (emotions) {
      setEmotion(emotions["stickers"][length]);
    }
  }, [emotions, length]);

  return (
    <div>
      <div>
        <h2>{props.article.title}</h2>
        <div>{props.article.text}</div>
      </div>
      <h3
        dangerouslySetInnerHTML={{
          __html: `Total Length ${length} ${emotion}`
        }}
      />
    </div>
  );
};

const Suboptimal: React.FC = () => {
  const articles = useGetArticles();
  const [currentArticle, setCurrentArticle] = useState<
    Articles["articles"]["0"] | null
  >();
  const onClickHandler = useCallback((article) => {
    setCurrentArticle(article);
  }, []);
  return (
    <div style={styles.container}>
      <Navigation articles={articles} onClickHandler={onClickHandler} />
      <div style={styles.content}>
        {/** Step 4. Using key to force destroy and recreate */}
        {currentArticle ? (
          <ArticleContent article={currentArticle} key={currentArticle.id} />
        ) : null}
      </div>
    </div>
  );
};

export default Suboptimal;

このパターンを使用すると、ちらつきの影響は回避されますが、小道具が変更されるたびに絵文字をフェッチするようにネットワーク要求が行われます。そのため、文字数に変更がない場合でも、同じ絵文字を取得するように不要なリクエストが行われます。

パターン#2:useMemoの依存関係としての小道具

今回は正しく、そして最適にやってみましょう。それはすべてアンチパターン#1から、propsまたはcontext初期状態として始まりました。

propsの依存関係として使用することで、これを修正できますuseMemo。合計文字数の計算をuseMemoフックに移動することで、平均の長さが変更されない限り、ネットワーク要求が絵文字をフェッチするのを防ぐことができます。

import { useCallback, useEffect, useMemo, useState } from "react";
import { Navigation } from "../components/Navigation";
import { useGetArticles } from "../hooks/useGetArticles";
import { useGetEmoji } from "../hooks/useGetEmoji";
import { Articles } from "../types";

const styles: { [key: string]: React.CSSProperties } = {
  container: {
    background: "#F0FDF4",
    height: "100%",
    display: "grid",
    gridTemplateColumns: "10rem auto"
  },
  content: {}
};

const ArticleContent: React.FC<{
  article: Articles["articles"]["0"];
}> = (props) => {
  // Step 1. calculate length as we need it to get corresponding emotion
  const length = useMemo<number>(
    () => props.article.text.length + props.article.title.length,
    [props]
  );

  // Step 2. fetch emotion map from backend
  const emotions = useGetEmoji();

  // Step 3. set emotion once we get emotion map from backend
  const [emotion, setEmotion] = useState<string>("");
  useEffect(() => {
    if (emotions) {
      setEmotion(emotions["stickers"][length]);
    }
  }, [emotions, length]);

  return (
    <div>
      <div>
        <h2>{props.article.title}</h2>
        <div>{props.article.text}</div>
      </div>
      <h3
        dangerouslySetInnerHTML={{
          __html: `Total Length ${length} ${emotion}`
        }}
      />
    </div>
  );
};

const Optimal: React.FC = () => {
  const articles = useGetArticles();
  const [currentArticle, setCurrentArticle] = useState<
    Articles["articles"]["0"] | null
  >();
  const onClickHandler = useCallback((article) => {
    setCurrentArticle(article);
  }, []);
  return (
    <div style={styles.container}>
      <Navigation articles={articles} onClickHandler={onClickHandler} />
      <div style={styles.content}>
        {currentArticle ? <ArticleContent article={currentArticle} /> : null}
      </div>
    </div>
  );
};

export default Optimal;

結論

この記事では、propsまたはcontextを初期状態として使用し、「破棄して再作成」をアンチパターンとして使用し、JSXで内部状態を使用propsし、依存関係として使用することuseMemoは適切なパターンであることを説明しました。また、依存関係の配列なしでフックを使用し、Reactコンポーネントを配置するためにネストする場合は、注意が必要であることも学びました。

このストーリーは、もともとhttps://betterprogramming.pub/how-we-reduced-bugs-in-our-react-code-base-9a7a979b4442で公開されました

#react #code 

Reactコードベースのバグをどのように削減したか
Saul  Alaniz

Saul Alaniz

1655258400

Cómo Redujimos Los Errores En Nuestra Base De Código React

Recientemente, mientras trabajábamos con nuestra gran base de código de la aplicación React, nos detuvimos en seco contra tres categorías de errores: no eran errores de tiempo de compilación o tiempo de ejecución, sino comportamientos de código inesperados.

  • Un componente no se actualiza con un evento de usuario.
  • Un componente se actualiza parcialmente con un evento de usuario.
  • Un componente se renderiza inesperadamente.

Nuestro primer instinto fue, por supuesto, "combatir el mal dondequiera que pudiéramos encontrarlo".

Sin embargo, incluso después de una letanía de declaraciones impresas, los errores seguían siendo difíciles de rastrear. Fue entonces cuando nos dimos cuenta de que ciertas partes de nuestro código podrían considerarse antipatrones.

Así que pasamos mucho tiempo entendiéndolos y caracterizándolos como tales para asegurarnos de evitar estos errores en el futuro.

Este artículo es un intento de explicar esos descubrimientos.

Patrones y Antipatrones en React

En este artículo, un código React califica como un buen patrón si:

  • El componente es reutilizable.
  • El código es más fácil de revisar y depurar.

Tenga en cuenta que el código todavía se considera un patrón si escribimos más líneas de código o (como era de esperar) introdujimos algunos renderizados adicionales para lograr los objetivos anteriores.

¿Por qué incluso los desarrolladores experimentados caen en la trampa de los antipatrones?

  1. El código React se ve sorprendentemente similar cuando sigue un patrón en comparación con el momento en que sigue un antipatrón.
  2. El patrón parece tan obvio que se ignora.

¿Cómo identificar los antipatrones?

Sugerencia n.º 1: enganche sin una matriz de dependencia

En React, diferentes piezas de código están vinculadas entre sí por dependencias. Estas piezas de código interdependiente juntas mantienen el estado de la aplicación en su forma deseada. Por lo tanto, si escribimos un fragmento de código en React que no tiene una dependencia, existe una alta probabilidad de que genere errores.

Por lo tanto, tenga cuidado cuando utilice ganchos como useState, useRefetc. porque no toman matrices de dependencias.

hombre gritando a otro.  subtítulo: y cuando abro el código, ¿qué encuentro?  ¡no hay una matriz de dependencia con este enlace!

Fuente: imgflip.com

Pista #2: Anidamiento sobre composición

Hay dos mecanismos mediante los cuales se organizan los componentes de React:

  1. Composición: Todos los niños tienen los mismos datos
  2. Anidamiento: cada niño puede tener datos diferentes

Imagen por autor

Imaginemos un escenario donde observamos que hay un error en “Niño 3”.

Si hubiéramos arreglado los componentes usando composición, no tendríamos que buscar en el código de “Niño 1” y “Niño 2” porque todos son independientes. Por lo tanto, la complejidad temporal de la depuración sería O (1).

Sin embargo, si hubiéramos arreglado los componentes mediante el anidamiento, tendríamos que verificar todos los elementos secundarios antes del "Niño 3" para descubrir el origen del error. En este caso, la complejidad temporal de la depuración sería O (n)dónde nestá el número de hijos por encima de "Niño 3".

Por lo tanto, podemos concluir que el anidamiento a menudo hace que el proceso de depuración sea más difícil que la composición.

Aplicación de ejemplo

Ahora, consideremos una aplicación para demostrar diferentes patrones y antipatrones.

Comportamiento deseado de la aplicación

Cuando se hace clic en un artículo en el menú de navegación izquierdo, se abre a la derecha. A esto le siguen dos acciones:

  1. Cálculo: El recuento total de caracteres del artículo se calcula (num_chars(title) + num_chars(text)y se muestra.
  2. Solicitud de red: en función del recuento total de caracteres del artículo, se obtiene un emoji a través de una solicitud de red y se muestra. A medida que aumenta el número de caracteres, el emoji cambia de triste a feliz.

Construyendo la aplicación

Llegaremos a la forma correcta de construir esta aplicación en cuatro pasos:

  • Incorrecto: la aplicación no funciona como se esperaba: no se activa ni el cálculo ni la solicitud de red cuando se selecciona un nuevo artículo.
  • Parcialmente correcto: la aplicación funciona como se esperaba, pero se observa un efecto de parpadeo cuando se selecciona un nuevo artículo.
  • Correcto pero subóptimo: la aplicación funciona como se esperaba sin parpadeos del DOM, pero realiza solicitudes de red innecesarias.
  • Correcto y óptimo: la aplicación funciona como se esperaba sin parpadeos de DOM ni solicitudes de red innecesarias.

Estructura del código

Puede abrir el sandbox anterior haciendo clic en el botón en la esquina inferior derecha.

El src/pagesdirectorio tiene páginas asignadas a cada paso. El archivo de cada página src/pagescontiene un ArticleContentcomponente. El código en discusión está dentro de este ArticleContentcomponente.

Repasemos ahora los antipatrones y patrones seguidos en los cuatro enfoques anteriores.

Anti-Patrón #1: Props o contexto como estado inicial

En el enfoque incorrecto, propso contextse ha utilizado como valor inicial para useStateo useRef. En la línea 27, podemos ver que la longitud promedio ha sido calculada y almacenada como un estado.

import { useCallback, useEffect, useState } from "react";
import { useGetArticles } from "../hooks/useGetArticles";
import { useGetEmoji } from "../hooks/useGetEmoji";
import { Articles } from "../types";
import { Navigation } from "../components/Navigation";

const styles: { [key: string]: React.CSSProperties } = {
  container: {
    background: "#FEE2E2",
    height: "100%",
    display: "grid",
    gridTemplateColumns: "10rem auto"
  },
  content: {}
};

const ArticleContent: React.FC<{
  article: Articles["articles"]["0"];
}> = (props) => {
  // Step 1. calculate length as we need it to get corresponding emotion
  const [length] = useState<number>(
    props.article.text.length + props.article.title.length
  );

  // Step 2. fetch emotion map from backend
  const emotions = useGetEmoji();

  // Step 3. set emotion once we get emotion map from backend
  const [emotion, setEmotion] = useState<string>("");
  useEffect(() => {
    if (emotions) {
      setEmotion(emotions["stickers"][length]);
    }
  }, [emotions, length]);

  return (
    <div>
      <div>
        <h2>{props.article.title}</h2>
        <div>{props.article.text}</div>
      </div>
      <h3
        dangerouslySetInnerHTML={{
          __html: `Total Length ${length} ${emotion}`
        }}
      />
    </div>
  );
};

const Incorrect: React.FC = () => {
  const articles = useGetArticles();
  const [currentArticle, setCurrentArticle] = useState<
    Articles["articles"]["0"] | null
  >();
  const onClickHandler = useCallback((article) => {
    setCurrentArticle(article);
  }, []);
  return (
    <div style={styles.container}>
      <Navigation articles={articles} onClickHandler={onClickHandler} />
      <div style={styles.content}>
        {currentArticle ? <ArticleContent article={currentArticle} /> : null}
      </div>
    </div>
  );
};

export default Incorrect;

Este antipatrón es la razón por la que ni el cálculo ni la solicitud de red se activan cuando se selecciona un nuevo artículo.

Anti-Patrón #2: Destruir y Recrear

Enmiendemos nuestro enfoque incorrecto utilizando el antipatrón 'Destruir y recrear'.

Destruir un componente funcional se refiere a destruir todos los ganchos y los estados creados durante la primera llamada a la función. Recrear se refiere a volver a llamar a la función como si nunca antes se hubiera llamado.

Tenga en cuenta que un componente principal puede usar el keyaccesorio para destruir el componente y volver a crearlo cada vez que keycambia. Sí, lo leíste bien: puedes usar teclas fuera de los bucles.

Específicamente, implementamos el antipatrón 'Destruir y recrear' usando el keyaccesorio mientras renderizamos el componente secundario ArticleContentdel componente principal RecreateDocen el RecreateDoc.tsxarchivo (línea 89).

import { useCallback, useEffect, useState } from "react";
import { Navigation } from "../components/Navigation";
import { useGetArticles } from "../hooks/useGetArticles";
import { useGetEmoji } from "../hooks/useGetEmoji";
import { Articles } from "../types";

const styles: { [key: string]: React.CSSProperties } = {
  container: {
    background: "#FEFCE8",
    height: "100%",
    display: "grid",
    gridTemplateColumns: "10rem auto"
  },
  content: {}
};

const ArticleContent: React.FC<{
  article: Articles["articles"]["0"];
}> = (props) => {
  // Step 2. fetch emotion map from backend
  const emotions = useGetEmoji();

  // Step 3, set emotion once we get emotion map from backend
  const [emotion, setEmotion] = useState<string>("");
  useEffect(() => {
    if (emotions) {
      setEmotion(
        emotions["stickers"][
          props.article.text.length + props.article.title.length
        ]
      );
    }
  }, [emotions, props]);

  return (
    <div>
      <div>
        <h2>{props.article.title}</h2>
        <div>{props.article.text}</div>
      </div>
      <h3
        dangerouslySetInnerHTML={{
          __html: `Total Length ${
            props.article.text.length + props.article.title.length
          } ${emotion}`
        }}
      />
    </div>
  );
};

const PartiallyCorrect: React.FC = () => {
  const articles = useGetArticles();
  const [currentArticle, setCurrentArticle] = useState<
    Articles["articles"]["0"] | null
  >();
  const onClickHandler = useCallback((article) => {
    setCurrentArticle(article);
  }, []);
  return (
    <div style={styles.container}>
      <Navigation articles={articles} onClickHandler={onClickHandler} />
      <div style={styles.content}>
        {currentArticle ? <ArticleContent article={currentArticle} /> : null}
      </div>
    </div>
  );
};

export default PartiallyCorrect;

La aplicación funciona como se esperaba, pero se observa un efecto de parpadeo cuando se selecciona un nuevo artículo. Por lo tanto, este antipatrón da como resultado una salida parcialmente correcta.

Patrón #1: Estado Interno en JSX

En lugar de usar el antipatrón 'Destruir y recrear', en este enfoque 'correcto pero subóptimo', usaremos 'reprocesar'.

Volver a renderizar se refiere a volver a llamar al componente funcional de reacción con los ganchos intactos en todas las llamadas a funciones. Tenga en cuenta que en 'Destruir y recrear', todos los ganchos se destruyen primero y luego se recrean desde cero.

Para implementar 'renderizado', useEffecty useStatese usará en tándem. El valor inicial de useStatepuede establecerse en nullo undefinedy se calculará y asignará un valor real una vez que se useEffecthaya ejecutado. En este patrón, estamos eludiendo la falta de matriz de dependencia useStatemediante el uso de useEffect.

Específicamente, observe cómo hemos movido el cálculo del recuento total de caracteres a JSX (línea 50) y estamos usando props(línea 41) como una dependencia en useEffect(línea 31).

import { useCallback, useEffect, useState } from "react";
import { Navigation } from "../components/Navigation";
import { useGetArticles } from "../hooks/useGetArticles";
import { useGetEmoji } from "../hooks/useGetEmoji";
import { Articles } from "../types";

const styles: { [key: string]: React.CSSProperties } = {
  container: {
    background: "#FEF2F2",
    height: "100%",
    display: "grid",
    gridTemplateColumns: "10rem auto"
  },
  content: {}
};

const ArticleContent: React.FC<{
  article: Articles["articles"]["0"];
}> = (props) => {
  // Step 1. calculate length as we need it to get corresponding emotion
  const [length] = useState<number>(
    props.article.text.length + props.article.title.length
  );

  // Step 2. fetch emotion map from backend
  const emotions = useGetEmoji();

  // Step 3. set emotion once we get emotion map from backend
  const [emotion, setEmotion] = useState<string>("");
  useEffect(() => {
    if (emotions) {
      setEmotion(emotions["stickers"][length]);
    }
  }, [emotions, length]);

  return (
    <div>
      <div>
        <h2>{props.article.title}</h2>
        <div>{props.article.text}</div>
      </div>
      <h3
        dangerouslySetInnerHTML={{
          __html: `Total Length ${length} ${emotion}`
        }}
      />
    </div>
  );
};

const Suboptimal: React.FC = () => {
  const articles = useGetArticles();
  const [currentArticle, setCurrentArticle] = useState<
    Articles["articles"]["0"] | null
  >();
  const onClickHandler = useCallback((article) => {
    setCurrentArticle(article);
  }, []);
  return (
    <div style={styles.container}>
      <Navigation articles={articles} onClickHandler={onClickHandler} />
      <div style={styles.content}>
        {/** Step 4. Using key to force destroy and recreate */}
        {currentArticle ? (
          <ArticleContent article={currentArticle} key={currentArticle.id} />
        ) : null}
      </div>
    </div>
  );
};

export default Suboptimal;

Con este patrón, se ha evitado el efecto de parpadeo, pero se realiza una solicitud de red para obtener emojis cada vez que cambian los accesorios. Por lo tanto, incluso si no hay cambios en el número de caracteres, se realiza una solicitud innecesaria para obtener el mismo emoji.

Patrón #2: accesorios como dependencia en useMemo

Hagámoslo correctamente y de manera óptima esta vez. Todo comenzó con Anti-Pattern #1: propso contextcomo estado inicial.

Podemos arreglar esto usando propscomo una dependencia en useMemo. Al mover el cálculo del recuento total de caracteres al useMemoenlace, podemos evitar que las solicitudes de red obtengan emojis a menos que la longitud promedio haya cambiado.

import { useCallback, useEffect, useMemo, useState } from "react";
import { Navigation } from "../components/Navigation";
import { useGetArticles } from "../hooks/useGetArticles";
import { useGetEmoji } from "../hooks/useGetEmoji";
import { Articles } from "../types";

const styles: { [key: string]: React.CSSProperties } = {
  container: {
    background: "#F0FDF4",
    height: "100%",
    display: "grid",
    gridTemplateColumns: "10rem auto"
  },
  content: {}
};

const ArticleContent: React.FC<{
  article: Articles["articles"]["0"];
}> = (props) => {
  // Step 1. calculate length as we need it to get corresponding emotion
  const length = useMemo<number>(
    () => props.article.text.length + props.article.title.length,
    [props]
  );

  // Step 2. fetch emotion map from backend
  const emotions = useGetEmoji();

  // Step 3. set emotion once we get emotion map from backend
  const [emotion, setEmotion] = useState<string>("");
  useEffect(() => {
    if (emotions) {
      setEmotion(emotions["stickers"][length]);
    }
  }, [emotions, length]);

  return (
    <div>
      <div>
        <h2>{props.article.title}</h2>
        <div>{props.article.text}</div>
      </div>
      <h3
        dangerouslySetInnerHTML={{
          __html: `Total Length ${length} ${emotion}`
        }}
      />
    </div>
  );
};

const Optimal: React.FC = () => {
  const articles = useGetArticles();
  const [currentArticle, setCurrentArticle] = useState<
    Articles["articles"]["0"] | null
  >();
  const onClickHandler = useCallback((article) => {
    setCurrentArticle(article);
  }, []);
  return (
    <div style={styles.container}>
      <Navigation articles={articles} onClickHandler={onClickHandler} />
      <div style={styles.content}>
        {currentArticle ? <ArticleContent article={currentArticle} /> : null}
      </div>
    </div>
  );
};

export default Optimal;

Conclusión

En este artículo, discutimos que usar propso contextcomo estado inicial y 'Destruir y recrear' son antipatrones mientras que usar el estado interno en JSX y propscomo una dependencia useMemoson buenos patrones. También aprendimos que debemos ser cautelosos cuando usamos ganchos sin una matriz de dependencia y anidamos para organizar los componentes de React.

Esta historia se publicó originalmente en https://betterprogramming.pub/how-we-reduced-bugs-in-our-react-code-base-9a7a979b4442

#react #code 

Cómo Redujimos Los Errores En Nuestra Base De Código React
Saul  Alaniz

Saul Alaniz

1654881540

Aumentar El Rendimiento De Su Código IOS: Reduzca La Búsqueda En Matri

La complejidad temporal de iterar sobre una matriz es O(n). Suena aceptable, ¿verdad? Podríamos descuidar su impacto en el rendimiento de nuestro código y no esforzarnos más en pensar en una mejor solución cuando tenemos muchos trabajos que hacer antes de la fecha límite. Pero una vez que usar esta habilidad se convierte en una respuesta natural, es posible que no nos demos cuenta de que incluso hemos escrito bucles for anidados o filtros anidados en nuestro código, lo que aumenta la complejidad del tiempo a O(n²), y es muy probable que suframos problemas de rendimiento. problemas más tarde.

Intente buscar en tipos de colección no secuenciales, como Sety Dictionary, en lugar de Array, ya que la complejidad temporal de buscar en ay a Setes dictionaryO(1), mientras que la complejidad temporal de buscar en an Arrayes O(n).

En este artículo, le mostraré cómo optimizar la velocidad con Sety dictionary.

Ejemplo 1: Conjunto

En el ejemplo 1, están los datos de todos los miembros y la identificación de los miembros platino. El objetivo es derivar una matriz de miembros platino de memberswith platinumMemberIDs.

let members: [Member]
let platinumMemberIDs:[Int]

Antes de la optimización

La complejidad temporal de filter(_:)es O(n), y la complejidad temporal del método de instancia de la matriz contains(_:)también es O(n).

El siguiente enfoque itera a través membersde array. En cada iteración, busca platinumMemberIDspara verificar si platinumMemberIDscontiene la identificación del miembro actual.

La complejidad temporal de este enfoque es O(n²).

var platinumMembers = members.filter {
    platinumMemberIDs.contains($0.id)
}

Después de la optimización

Dado que el orden de platinumMemberIDsno importa, podemos mantener estos datos en el tipo de recopilación no secuencial Set. La complejidad temporal de la búsqueda en a Setes O(1). La complejidad temporal total se reduce a O(n).

let platinumMemberIDSet = Set(platinumMemberIDs)var platinumMembers = members.filter {
    platinumMemberIDSet.contains($0.id)
}

Ejemplo 2: Diccionario

En el ejemplo 2, hay dos grupos de usuarios que provienen de diferentes fuentes y nos gustaría conocer su intersección. Siempre que el correo electrónico de un usuario sea el mismo que el de otro usuario del otro grupo, suponemos que es el mismo usuario.

let starUsers: [User]
let recentUsers: [User]

Antes de la optimización

La forma más sencilla es escribir bucles for anidados para averiguar los usuarios duplicados, pero la complejidad del tiempo será O(n²), no muy ideal.

var duplicatedUsers = [User]()for star in starUsers {
  for recent in recentUsers {
    if star.email == recent.email {
      duplicatedUsers.append(star)
    }
  }
}

Después de la optimización

Para reducir la complejidad del tiempo, simplemente podemos crear un diccionario a partir de una recentUsersmatriz con el correo electrónico de cada usuario como clave y el objeto de usuario como valor.

let recentUserMap = Dictionary(uniqueKeysWithValues: recentUsers.map { ($0.email, $0) })

Y luego buscamos a través de la starUsersmatriz, pero en cada iteración, no necesitamos iterar sobre la recentUsersmatriz para encontrar los usuarios duplicados después de la optimización. En su lugar, accedemos al objeto de usuario de un usuario reciente cuyo correo electrónico es el mismo que el del usuario estrella actual directamente desde recentUserMap. La complejidad del tiempo se reduce a O(n).

let duplicatedUsers = starUsers.filter {
  guard let _ = recentUserMap[$0.email] else { return false }
  return true
}

Esta historia se publicó originalmente en https://betterprogramming.pub/how-to-boost-your-ios-code-performance-reduce-searching-an-array-55fbdfee2050

#ios #code #swift 

Aumentar El Rendimiento De Su Código IOS: Reduzca La Búsqueda En Matri

10 Consejos Para Mejorar Las Solicitudes De Incorporación De Cambios

La revisión de código es un enfoque ampliamente adoptado en el mundo del desarrollo de software. Hacer que otros desarrolladores verifiquen su código (y que verifiquen el de ellos a su vez) ayuda a eliminar errores, limpiar la base de código y compartir conocimientos con todo el equipo. Pero a pesar de lo útil que es, la revisión del código aún puede ser bastante estresante y llevar mucho tiempo. En este artículo, me gustaría compartir algunos consejos e ideas sobre cómo hacer que el proceso de revisión de código sea lo más simple y sencillo posible, tanto para quienes revisan como para quienes preparan el código para su revisión.

¿Por qué necesita revisión de código?

Hay debates sobre la necesidad de solicitudes de incorporación de cambios y revisiones de código, y algunos desarrolladores sugieren la programación en parejas y la programación en masa como alternativas. ( Wikipedia : La programación mafiosa es un enfoque de desarrollo de software en el que todo el equipo trabaja en lo mismo, al mismo tiempo, en el mismo espacio y en la misma computadora) .

Si bien el objetivo de este artículo no es comparar estos enfoques, quiero resaltar algunos beneficios del proceso de revisión de código.

  1. Intercambio de conocimientos de la base de código: en caso de que la programación mafiosa no sea su enfoque preferido, las revisiones de código pueden ayudarlo a mantener actualizado su conocimiento de la base de código.
  2. Capacidad de mantenimiento del código: los revisores de código son las personas más cercanas a quienes mantendrán el código base en el futuro, es decir, sin memoria de la implementación de una característica determinada. Su revisión puede brindar información sobre la capacidad de mantenimiento del código y cómo se puede mejorar.
  3. Segundo par de ojos: Los humanos somos los responsables de escribir el código (por ahora). Podemos cansarnos y nuestros ojos pueden ponerse borrosos, especialmente cuando trabajamos en algo durante mucho tiempo. Un segundo par de ojos puede captar cosas que nos perdimos debido a un error humano. También puede ser útil que otra persona revise nuestro trabajo si tiene más conocimiento o experiencia con una característica específica o pieza de código.
  4. Intercambio de conocimientos de programación: las revisiones de código son una excelente manera para que los desarrolladores más experimentados transmitan sus conocimientos y mejores prácticas a los desarrolladores menos experimentados. No puede supervisar todo el proceso de implementación de funciones, ni puede planificar todos los detalles por adelantado. Es por eso que las retrospectivas del código de los desarrolladores pueden contribuir en gran medida a su crecimiento. Les da la oportunidad de ensuciarse las manos con la implementación y aprender mejores prácticas a través de la revisión del código.
  5. Documentación: aunque es más una consecuencia implícita que una causa directa, si se hace correctamente, el historial de solicitudes de incorporación de cambios puede servir como documentación adicional para el código base.

Enfoques de revisión de código

He encontrado diferentes enfoques para la revisión de código, según la solicitud de incorporación de cambios, la preferencia del revisor, el tiempo disponible, etc.

  1. Revisión por confirmación: esto solo se puede hacer si el historial de confirmación está disponible y si es lo suficientemente claro.
  2. Revisar por archivo: Esto es más fácil de hacer si la solicitud de extracción no es muy grande.
  3. Verifique el código localmente: este enfoque consume la mayor parte del tiempo y el esfuerzo, pero a veces es la única forma de comprender realmente lo que está sucediendo.

La empatía como guía

La revisión del código es un proceso de comunicación puramente humano. Y una buena manera de acercarse a los humanos (especialmente a aquellos con los que necesita trabajar) es con empatía. Esto incluye lo obvio: cortesía, amabilidad y respeto. Pero puede ir un paso más allá y recordar que cuando asigna revisores para revisar su código, está solicitando el tiempo y la energía de otra persona. Por lo tanto, es bueno facilitarles el proceso y hacerlo probablemente resulte en una revisión de código más eficiente. Entonces, teniendo en cuenta la empatía, analicemos algunos consejos prácticos que pueden hacer que sus revisores de código estén más felices.

Expectativas

En general, todos estos consejos se pueden aplicar a casi cualquier tipo de sistema de desarrollo y control de versiones (VCS). Este artículo constará de dos partes: la teoría y la práctica. Para el ejemplo práctico, crearé una aplicación Flutter usando GitHub como cliente Git y Codemagic como herramienta CI/CD.

Parte 1: antes y durante la codificación

1. Estrategia de denominación de sucursales

Entonces abrió su administrador de tareas, seleccionó un ticket y lo movió a "Progreso". ¿Que sigue?

Crea una sucursal en la que trabajarás. Y llamar a esa sucursal... ¿qué?

El nombre de la rama es su primera oportunidad para dar contexto a su tarea. En caso de que su revisor decida verificar el código localmente, deberá encontrar esa rama entre muchas otras.

Hay varios buenos enfoques entre los que puede elegir, dependiendo de cómo administre el trabajo.

Enfoque 1:

Si está usando una herramienta de administración de tareas que le da a sus tareas un prefijo o un número (por ejemplo, Jira), también puede usar ese prefijo, especialmente para configurar algunos enlaces, como mover el ticket a "Revisión de código" cuando se abre un PR o "Para probar" cuando se fusiona el PR. Por ejemplo, si el nombre de la tarea es ABC-57: Add list with purchases, un buen nombre para una sucursal sería ABC-57_purchases_list.

Pero ¿por qué no solo ABC-57?

Bueno, comparemos estas dos opciones.

Lista de sucursales A:Lista de sucursales B:
ABC-57ABC-57_purchases_list
ABC-64ABC-64_fix_cart_npe
ABC-73ABC-73_migrate_2.8

¿En cuál de estas listas encontrará más rápido la sucursal requerida? El uso de una convención de nomenclatura que incluya una descripción también ayuda cuando cambia sus propias sucursales con regularidad, ya que no necesita recordar ni buscar el número de ticket.

Enfoque 2:

Use el mismo enfoque prefix+ description, pero en lugar de anteponer el nombre con el número de boleto, prefije con lo que es: feature, bug_fix, refactor, etc. Es mejor usar este enfoque solo si no hay nombres de boletos disponibles. Aquí hay un par de ejemplos:

feature_purchases_listobug_fix_cart_npe

Enfoque 3:

Cualquier otra estrategia de nomenclatura de ramas está bien, siempre que sea consistente y tenga una estructura lógica. Nombrar ramas al azar puede eventualmente conducir al caos. No solo es más difícil navegar entre ellos, sino también más difícil de mantener. ¿Qué ramas están rancias? ¿Puedes eliminarlos? ¿Hay trabajo no fusionado? ¿Sigue siendo relevante?

2. Historial de confirmaciones

Una estrategia de revisión de código que puede usar es la revisión por confirmación, pero solo si el historial de confirmación lo hace posible. Aquí hay un artículo realmente excelente y detallado sobre cómo nombrar correctamente sus confirmaciones.

En resumen, aquí hay algunas pautas:

  1. Mantenga los mensajes de confirmación concisos. Lo mejor es mantenerlos alrededor de 50 caracteres, y 72 es el límite estricto. Todo lo que sea más de 72 está envuelto en GitHub.
  2. Utilice un enfoque imperativo para sus mensajes de confirmación. Expréselo como lo que sucederá cuando aplique este compromiso, por ejemplo, Update headline text coloro Remove unused imports.
  3. Esta estrategia de nomenclatura está estrechamente relacionada con una noción aún más importante para administrar el historial: confirmaciones atómicas. No puede dar un nombre conciso a una confirmación a menos que introduzca un solo cambio lógico, es decir, es atómico.
  4. Si tiene nombres de tickets, como se menciona en las estrategias de nomenclatura de sucursales, prefije cada confirmación con el nombre del ticket. Esto puede facilitar la navegación por el historial de confirmaciones por tarea y permitir integraciones con herramientas de gestión de proyectos. Por ejemplo, puede integrar Jira con GitHub para que las descripciones de los tickets también realicen un seguimiento del historial de confirmaciones.

Con solo leer el historial de confirmaciones de la solicitud de extracción, el revisor ya puede obtener una cierta comprensión de lo que revisará, incluso antes de ver una sola línea de código.

Si desea ir un paso más allá, recientemente encontré un enfoque interesante para las confirmaciones. El contexto de este enfoque es más amplio que solo nombrar compromisos. Puedes leer todo sobre esto aquí .

Pero la idea general es escribir un nombre de confirmación antes de comenzar a codificar. Esto lo ayudará a mantenerse enfocado en un paso a la vez, brindará más claridad sobre lo que debe hacer e implícitamente hará que sus confirmaciones sean atómicas. Creo que esta es una toma muy interesante y planeo intentarlo yo mismo.

Parte 2: Antes de asignar los revisores

Lo primero que debe tener en cuenta es que usted, a diferencia del revisor, está actualmente inmerso en el contexto de su tarea y su solución. Pero cuando el revisor abre tu solicitud de extracción, todo lo que tiene es lo que le das. No escribieron el código y no pasaron X horas o días trabajando en el problema. Por lo tanto, es importante brindar el mayor contexto posible para que la revisión sea más útil.

3. Proporcione contexto: descripción e imágenes

Descripción

Cuando hablo de crear una descripción, no me refiero a simplemente vincular el ticket de Jira al PR y terminar con él. Esto requeriría que el revisor cambie a Jira y lea la descripción, que podría tener más información de la necesaria para la revisión del código... y, sin embargo, no daría ninguna pista sobre cómo se implementa la solución.

Puede evitar esto proporcionando una breve descripción y respondiendo tres preguntas:

  • ¿Qué? ¿Cuál es la tarea que se lleva a cabo con esta solicitud de extracción?
  • ¿Cómo? ¿Cómo se implementa (una descripción general de su solución)?
  • ¿Por qué? Si corresponde (por ejemplo, si existen varios enfoques válidos), ¿por qué eligió este enfoque o cuáles son los impedimentos de los otros enfoques?

Gracias a esta descripción, el revisor sabrá qué buscar en el código y por qué se escribió de esa manera. Esto les permite proporcionar comentarios más relevantes.

Puede asegurarse de esto agregando una plantilla de solicitud de extracción en GitHub.

Imágenes y videos

Evaluar el código sabiendo cómo se ve el resultado real es mucho más fácil que intentar imaginarlo. Si su tarea está relacionada con cambios en la interfaz de usuario, agregar una imagen del resultado final (o un video, si es un flujo) beneficiará en gran medida la comprensión del código por parte del revisor. O, si su tarea implica una lógica complicada, puede adjuntar un diagrama de secuencia que explique el algoritmo.

4. Mantenlo pequeño

Cuanto mayor sea la solicitud de incorporación de cambios, peor será la revisión del código. La revisión del código es mentalmente un proceso difícil: debe leer el código, descubrir qué está haciendo, comprender cómo lo está haciendo y buscar posibles problemas. Cuantas más líneas de código necesites tener en cuenta, mayor será la posibilidad de que pases algo por alto. Aunque en realidad no hay un número "dorado" de líneas, recomendaría ceñirse a menos de 500. Si hay más, divídalas en varias solicitudes de extracción.

5. Mantenlo limpio

No, no estoy hablando de la arquitectura aquí. Durante el desarrollo, usamos todo tipo de trucos: generar registros de impresión, codificar valores específicos, dejar TODOs, etc. Para ahorrar algo de tiempo y mantener el enfoque del revisor en la función en cuestión, acostúmbrese a asegurarse de que su solicitud de extracción esté limpia antes de asignar revisores. ¿Qué quiero decir con mantenerlo limpio?

  1. Encuentre y elimine todos los registros de depuración y el código no utilizado o comentado.
  2. Decide qué hacer con TODO:
    1. Impleméntalo.
    2. Si no puede implementarlo de inmediato, cree una tarea o deje al menos algún tipo de estimación sobre cuándo se realizará.
    3. Elimínelo si está hecho o ya no es relevante.
  3. Con respecto a las cadenas codificadas (aunque no todos los posibles valores codificados), recientemente me topé con un consejo de Vandad Nahavandipoor sobre el sufijo de todas sus cadenas codificadas con un emoji (con la ayuda de una función de extensión, por ejemplo).

https://raw.githubusercontent.com/vandadnp/flutter-tips-and-tricks/main/images/hardcoded-strings-in-flutter.jpg#center

Consejo profesional: la mayoría de esos problemas se pueden resolver con herramientas de análisis de código estático, aunque algunos de ellos requieren la implementación de reglas personalizadas, según cómo desee manejarlos.

6. Resuelva todos los conflictos de combinación

Esto puede parecer algo menor porque la función está lista, el código es definitivo y puede dejar los conflictos de fusión para más adelante... Pero eso no siempre es cierto. Su rama puede haber divergido mucho de la rama base, o algunos cambios pueden chocar con su código de una manera que hace que sea imposible fusionarse sin refactorizar. Y esto requerirá otra revisión o se fusionará con problemas posiblemente pasados ​​por alto.

7. Excluir archivos de generación de código

No es necesario revisar los archivos generados por código, como los modelos JSON o los simulacros de pruebas unitarias. Por lo tanto, verlos explícitamente en una diferencia no agrega ningún beneficio y solo es molesto. Diferentes clientes de Git tienen diferentes enfoques para excluir dichos archivos de la revisión. Por ejemplo, en GitHub, puede agregar los patrones a.gitattributes , y se colapsarán en la diferencia final.

https://pbs.twimg.com/media/FKwn0QDXEAUi_4q?format=jpg&name=large

8. Análisis de código estático

Es una buena práctica tener habilitado el análisis de código estático. Los documentos de Dart describen sus ventajas de manera muy concisa:

El análisis estático le permite encontrar problemas antes de ejecutar una sola línea de código. Es una poderosa herramienta que se utiliza para prevenir errores y garantizar que el código se ajuste a las pautas de estilo.

Los nuevos proyectos de Dart y Flutter tienen reglas de lint habilitadas de forma predeterminada. Puede leer cómo habilitarlos en proyectos existentes aquí . Consulta las reglas de Dart aquí y de Flutter aquí . Puede crear sus propias reglas o deshabilitar algunas existentes.

Antes de asignar revisores, asegúrese de que su código no viole ninguna regla de lint. Puede asegurarse de esto con CI: bloquee la posibilidad de fusionarse hasta que no haya advertencias de pelusa.

Consejo profesional: puede ir más allá con herramientas como Dart Code Metrics .

9. Pruebas unitarias

Las pruebas unitarias ayudan a identificar problemas y detectar errores en las primeras etapas de desarrollo antes de que alguien más vea el código, y mucho menos lo pruebe. Además, aseguran que el comportamiento del código permanezca igual en caso de cambios en el código. Como beneficio adicional, también hacen que escriba un código más limpio y más desacoplado, lo que contribuye a la calidad general del código.

Puede configurar CI para ejecutar pruebas unitarias en cada solicitud de extracción y bloquear la fusión en caso de que algo falle. Y debe corregir cualquier prueba fallida antes de marcar la solicitud de extracción como lista para revisión.

Consejo profesional: habilite los informes de cobertura de código con herramientas como Codecov .

10. Otros marcadores, como hitos y etiquetas

El uso de marcadores como hitos y etiquetas es más un consejo agradable y, según el tamaño del proyecto, se puede omitir. Pero en proyectos más grandes, puede ayudar a organizar las solicitudes de incorporación de cambios. Por ejemplo, si hay muchas solicitudes de incorporación de cambios para revisar, se pueden filtrar por hito para que las relaciones públicas de la próxima versión se revisen antes. Y las etiquetas pueden proporcionar más contexto al indicar errores, funciones, mejoras, etc.

Consejo de bonificación

Revise su propio código antes de asignar otros revisores. Sí, acabas de escribir este código y aún recuerdas todo. Pero darle una nueva apariencia, especialmente en la GUI del cliente Git, puede ayudarlo a encontrar algunas cosas que puede haber pasado por alto anteriormente.

Parte 3: Después de la revisión

Aborde y resuelva todos los comentarios antes de la fusión. De esta manera, no dejará que nadie adivine qué se implementó y qué no. Y al abordar no quiero decir que tengas que implementar cada comentario, pero al menos deja una respuesta o reacciona con un emoji para indicar que se manejó o no se manejará. Luego, el revisor puede resolver la conversación. GitHub incluso tiene una configuración que puede habilitar que bloquea la fusión a menos que se hayan resuelto todas las conversaciones.

Consejos varios

A continuación, puede encontrar una lista muy pequeña de otras cosas que puede hacer para mejorar la revisión y la calidad del código.

Al escribir código, trate de leerlo. Si tiene que calcular mucho mientras lee, entonces considere refactorizarlo. Computar código sobre la marcha significa mantener muchas cosas en la memoria, y la memoria de trabajo humana es muy limitada. Esto significa que cuantos más cálculos necesite realizar, mayor será la probabilidad de que se pierda algo del panorama general, lo que hará que la revisión del código sea menos efectiva. Como revisor, si me resulta difícil mantener en la memoria todo lo que está sucediendo, pido refactorizar el código. (Algunos ejemplos de código que necesita refactorización son comprobaciones booleanas complejas o cuerpos de métodos largos).

Tenga una lista de verificación de cosas para revisar y probar antes de abrir una solicitud de extracción. Para una aplicación móvil, esto puede incluir configuraciones como retrato y paisaje, niveles de API mínimos y máximos del sistema operativo, sabores y temas. Tengo una publicación sobre mi propia lista de verificación , que es muy antigua pero aún relevante.

GitHub tiene una función llamada borrador de solicitudes de extracción. Si desea una revisión temprana, quizás sobre una característica complicada, pero el código aún no está listo para la revisión, puede marcar la solicitud de incorporación de cambios como borrador. Creo que esta es una solución más ordenada que prefijar PR con títulos como WIP.

Integre otras herramientas para controlar la calidad del código. Para los desarrolladores de Flutter, estos pueden ser Dart Code Metrics , Spec de Invertase o varias herramientas de Very Good Ventures .

Parte 4: un proyecto de muestra para mostrar buenas prácticas de solicitud de extracción

Ahora configuremos un proyecto de muestra que muestre algunos de estos consejo

  1. Exija que el análisis de código estático y la prueba de unidad pasen antes de fusionarse con la ayuda de Codemagic.
  2. Ejecute automáticamente las comprobaciones en cada cambio de solicitud de extracción a través de webhooks.
  3. Use una plantilla de solicitud de extracción en cada nueva solicitud de extracción.
  4. Y vea algunos ejemplos de solicitudes de incorporación de cambios.

Paso 1: crea un repositorio con un proyecto de Flutter en GitHub

https://github.com/darjaorlova/codemagic_pr_todo

Paso 2: Agrega el proyecto a Codemagic

Captura de pantalla 2022-02-13 en 22.27.12.png#center

Codemagic tiene una opción de Editor de flujo de trabajo para Flutter, lo que hace que sea muy fácil configurar una canalización de CI/CD completa para proyectos de Flutter utilizando una GUI bastante intuitiva. Entonces, en aras de la simplicidad, usaremos el Editor de flujo de trabajo de Codemagic aquí.

Paso 3: Cree un flujo de trabajo para solicitudes de incorporación de cambios

En Codemagic, haga clic en Duplicar flujo de trabajo .

Captura de pantalla 2022-02-13 en 22.28.36.png#center

Haga clic en el nombre del flujo de trabajo y cámbielo a Pull request.

Captura de pantalla 2022-02-13 a las 22.30.17.png#center

Paso 4: Configure el flujo de trabajo para ejecutar solo pruebas

En el editor de flujo de trabajo de Codemagic, seleccione la casilla de verificación Ejecutar solo pruebas .

Captura de pantalla 2022-02-13 en 22.31.03.png#center

Expanda la pestaña Desencadenadores de compilación y marque las siguientes casillas de verificación:

  • Gatillo al empujar
  • Desencadenar en actualización de solicitud de extracción
  • Cancelar compilaciones de webhook obsoletas

Expanda la pestaña Pruebas y asegúrese de que la opción Detener compilación si fallan las pruebas o el análisis esté marcada.

Captura de pantalla 2022-02-13 en 22.32.02.png#center

Marque la casilla de verificación Habilitar analizador Flutter en la pestaña Análisis de código estático .

Captura de pantalla 2022-02-13 en 22.33.53.png#center

Marque la casilla de verificación Habilitar prueba Flutter en la pestaña Pruebas unitarias e integración .

Captura de pantalla 2022-02-13 en 22.34.45.png#center

Y eso es todo para la configuración del flujo de trabajo. ¡No olvides guardar los cambios!

Captura de pantalla 2022-02-13 en 22.35.39.png#center

Comencemos nuestra primera compilación para asegurarnos de que funcione.

Captura de pantalla 2022-02-13 en 22.48.48.png#center

Paso 5: Configurar reglas de rama en GitHub

Importante: Las reglas de protección de sucursales solo se pueden establecer si tiene una cuenta de GitHub Pro o Teams.

Abra ConfiguraciónSucursalesAgregar regla .

Captura de pantalla 2022-02-13 en 22.39.07.png#center

Marca todas las casillas que necesites. En nuestro caso, son:

  • Requerir una solicitud de extracción antes de la fusión
  • Requerir aprobaciones: 1
  • Requerir verificaciones de estado para pasar antes de la fusión
  • Requerir que las sucursales estén actualizadas antes de fusionarse
  • Requerir resolución de conversación antes de fusionar

Complete el nombre de la sucursal y guarde.

Captura de pantalla 2022-02-13 en 22.43.02.png#center

En este punto, nuestra base de código consiste en el ejemplo básico de la aplicación de contador Flutter. Probemos la integración:

  1. Crea una rama con el prefijo test_codemagic_integration. (Aquí testhay un prefijo que usaré para las sucursales que están probando algo sin introducir funciones).
  2. Agregue una importación no utilizada o cualquier otra cosa que genere una advertencia de pelusa y empújela.
  3. Crea una solicitud de extracción. Deberíamos ver que la comprobación ha fallado.

Este es el PR: https://github.com/darjaorlova/codemagic_pr_todo/pull/1

Captura de pantalla 2022-02-13 en 22.58.23.png#center

Y si lo comprobamos en la consola de Codemagic, veremos que la compilación falló:

Captura de pantalla 2022-02-13 en 22.59.11.png#center

En los detalles de la compilación, podemos ver que el análisis de Flutter ha encontrado problemas .

Captura de pantalla 2022-02-13 a las 23.00.30.png#center

En este momento, es posible que tenga un par de preguntas: ¿Cómo sabía Codemagic cuándo ejecutar el flujo de trabajo y cómo informaba a GitHub? La respuesta es a través de webhooks . Se agregó automáticamente un webhook cuando conectamos nuestro proyecto a GitHub y, en los primeros pasos, configuramos los disparadores para ese webhook.

Puede verificarlo en la configuración del proyecto Codemagic:

Captura de pantalla 2022-02-13 en 23.03.15.png#center

Y en GitHub: ConfiguraciónWebhooks

Captura de pantalla 2022-02-13 a las 23.05.00.png#center

Esta es una verificación de estado que establecemos según lo requerido en las reglas de protección de sucursales.

Paso 6: agregue la plantilla de relaciones públicas de GitHub

  1. En la carpeta raíz de nuestro proyecto, debemos agregar un archivo llamado pull_request_template.md.
  2. Rellénelo (confirmar, empujar y fusionar en main).
## Description

1. What type of code this is (e.g., bug fix, new feature)
2. What it does and how it does it
3. Why it is done that way (if required)
4. Nice to have: images/videos (if it involves the UI) 

## Checklist

- [ ] Has unit tests
- [ ] Coverage is at least 90%
- [ ] Has documentation 
- [ ] Has release milestone

La próxima vez que abramos una solicitud de extracción, ya estará precargada para nosotros:

Captura de pantalla 2022-02-13 a las 23.21.45.png#center

Y el resultado se ve así: https://github.com/darjaorlova/codemagic_pr_todo/pull/3

Captura de pantalla 2022-02-13 en 23.26.12.png#center

Conclusión

Ya hemos terminado con la integración. Espero que hayas aprendido algo nuevo. Estos consejos para hacer que las solicitudes de incorporación de cambios sean más claras y fáciles de revisar ayudarán a simplificar su vida como desarrollador. ¡Feliz solicitud de extracción!

Si desea replicar este proyecto y ver ejemplos, el repositorio de GitHub con el codemagic.yamlarchivo está disponible aquí

Esta historia se publicó originalmente en https://blog.codemagic.io/10-tips-for-better-pull-requests-and-code-review/

#code #review 

10 Consejos Para Mejorar Las Solicitudes De Incorporación De Cambios
坂本  篤司

坂本 篤司

1654828260

プルリクエストとコードレビューを改善するための10のヒント

コードレビューは、ソフトウェア開発の世界で広く採用されているアプローチです。仲間の開発者にあなたのコードをチェックさせる(そして彼らのコードを順番にチェックさせる)ことは、間違いを排除し、コードベースをクリーンアップし、チーム全体で知識を共有するのに役立ちます。しかし、それでも役立つのですが、コードレビューは依然として非常にストレスがかかり、時間がかかる可能性があります。この記事では、コードレビュープロセスを可能な限りシンプルで苦痛のないものにするためのヒントとアイデアを共有したいと思います。レビューする人とレビューのためにコードを準備する人の両方にとってです。

なぜコードレビューが必要なのですか?

プルリクエストとコードレビューの必要性については議論が交わされており、一部の開発者はペアプログラミングとmobプログラミングを代替案として提案しています。ウィキペディアMobプログラミングは、チーム全体が同じこと、同時に、同じスペース、同じコンピューターで作業するソフトウェア開発アプローチです)

この記事の目的はこれらのアプローチを比較することではありませんが、コードレビュープロセスのいくつかの利点を強調したいと思います。

  1. コードベースの知識の共有:暴徒のプログラミングが好ましいアプローチではない場合、コードレビューはコードベースの知識を最新の状態に保つのに役立ちます。
  2. コードの保守性:コードレビューアは、将来コードベースを保守する人に最も近い人です。つまり、特定の機能の実装のメモリがありません。彼らのレビューは、コードの保守性とそれをどのように改善できるかについての洞察を与えることができます。
  3. 2番目の目:私たち人間は(今のところ)コードを書く責任があります。特に長時間何かに取り組んでいると、疲れたり、目がぼやけたりすることがあります。2つ目の目は、人為的ミスのために見逃したものを捉えることができます。また、特定の機能やコードの一部についてより多くの知識や経験を持っている人がいる場合は、他の人に私たちの仕事を見てもらうことも役立ちます。
  4. プログラミング知識の共有:コードレビューは、上級開発者が知識とベストプラクティスを経験の浅い開発者に渡すための優れた方法です。機能の実装プロセス全体を監督することも、すべての詳細を事前に計画することもできません。これが、開発者のコ​​ードの回顧が彼らの成長に大きく貢献できる理由です。それは彼らに実装で彼らの手を汚し、コードレビューを通してより良い実践を学ぶ機会を与えます。
  5. ドキュメント:直接的な原因というよりは暗黙の結果ですが、正しく実行された場合、プルリクエストの履歴はコードベースの追加のドキュメントとして機能します。

コードレビューアプローチ

プルリクエスト、レビュー担当者の好み、利用可能な時間などに応じて、コードレビューへのさまざまなアプローチに遭遇しました。

  1. コミットごとのレビュー:これは、コミット履歴が利用可能であり、それが十分に明確である場合にのみ実行できます。
  2. ファイルごとのレビュー:プルリクエストがそれほど大きくない場合、これは簡単に実行できます。
  3. コードをローカルでチェックしてください:このアプローチは最も時間と労力を消費しますが、実際に何が起こっているのかを理解する唯一の方法である場合があります。

ガイドラインとしての共感

コードレビューは、純粋に人間のコミュニケーションプロセスです。そして、人間(特にあなたが一緒に働く必要がある人間)にアプローチする良い方法は、共感することです。これには明らかなことも含まれます:礼儀正しさ、優しさ、そして敬意。しかし、さらに一歩進んで、コードを調べるためにレビュー担当者を割り当てるときは、他の人の時間とエネルギーを要求していることを覚えておいてください。したがって、プロセスを簡単にするのは良いことであり、そうすることで、おそらくより効率的なコードレビューが得られます。ですから、共感を念頭に置いて、コードレビューアを幸せにするための実用的なヒントについて話し合いましょう。

期待

一般に、これらのヒントはすべて、ほぼすべてのタイプの開発およびバージョン管理システム(VCS)に適用できます。この記事は、理論と実践の2つの部分で構成されます。実際の例として、GitHubをGitクライアントとして使用し、CodemagicをCI/CDツールとして使用してFlutterアプリケーションを作成します。

パート1:コーディング前とコーディング中

1.ブランチネーミング戦略

これで、タスクマネージャーを開き、チケットを選択して、「進行状況」に移動しました。次は何ですか?

作業するブランチを作成します。そして、そのブランチを…何と呼びますか?

ブランチ名は、タスクのコンテキストを与える最初の機会です。レビュー担当者がコードをローカルでチェックアウトすることを決定した場合、レビュー担当者は他の多くのブランチの中からそのブランチを見つける必要があります。

作業の管理方法に応じて、いくつかの優れたアプローチから選択できます。

アプローチ1:

タスクにプレフィックスまたは番号を付けるタスク管理ツール(Jiraなど)を使用している場合は、そのプレフィックスを使用することもできます。特に、チケットを「コードレビュー」に移動するなどのフックを設定する場合に使用できます。 PRが開かれるか、PRがマージされるときに「テストする」。たとえば、タスク名がABC-57: Add list with purchases、の場合、ブランチの適切な名前は。になりますABC-57_purchases_list

しかし、なぜだけではないのABC-57ですか?

では、これら2つのオプションを比較してみましょう。

ブランチのリストA:ブランチBのリスト:
ABC-57ABC-57_purchases_list
ABC-64ABC-64_fix_cart_npe
ABC-73ABC-73_migrate_2.8

これらのリストのうち、必要なブランチをより早く見つけることができるのはどれですか?説明を含む命名規則を使用すると、チケット番号を覚えたり調べたりする必要がないため、自分のブランチを定期的に切り替えるときにも役立ちます。

アプローチ2:

同じprefix+アプローチを使用しますが、名前の前にチケット番号を付ける代わりに、、、、descriptionなどのプレフィックスを付けます。このアプローチは、使用可能なチケット名がない場合にのみ使用することをお勧めします。次にいくつかの例を示します。featurebug_fixrefactor

feature_purchases_listまたbug_fix_cart_npe

アプローチ3:

一貫性があり、論理構造を持っている限り、他のブランチ命名戦略は問題ありません。ブランチにランダムに名前を付けると、最終的には混乱につながる可能性があります。それらの間を移動するのが難しいだけでなく、維持するのもより困難です。どのブランチが古くなっていますか?それらを削除できますか?マージされていない作業はありますか?それはまだ関連していますか?

2.コミット履歴

使用できるコードレビュー戦略の1つは、コミットごとのレビューですが、コミット履歴によって可能になる場合に限ります。これは、コミットに正しく名前を付ける方法についての非常に優れた詳細な記事です。

要するに、ここにいくつかのガイドラインがあります:

  1. コミットメッセージは簡潔にしてください。それらを約50文字に保つのが最善であり、72が厳しい制限です。72を超えるものはすべてGitHubでラップされます。
  2. コミットメッセージには必須のアプローチを使用してください。このコミットを適用したときに何が起こるかを表現します。たとえば、Update headline text colorまたはRemove unused imports
  3. この命名戦略は、履歴を管理するためのさらに重要な概念であるアトミックコミットと緊密に結合されています。単一の論理的な変更を導入しない限り、つまりアトミックでない限り、コミットに簡潔な名前を付けることはできません。
  4. ブランチの命名戦略で述べたように、チケット名がある場合は、すべてのコミットの前にチケットの名前を付けます。これにより、タスクごとにコミット履歴をナビゲートしやすくなり、プロジェクト管理ツールとの統合が可能になります。たとえば、JiraをGitHubと統合して、チケットの説明でコミット履歴も追跡できるようにすることができます。

プルリクエストのコミット履歴を読み取るだけで、レビュー担当者は、コードが1行表示される前であっても、レビュー対象についてある程度理解できます。

さらに一歩進めたい場合は、最近、コミットに対する興味深いアプローチに遭遇しました。このアプローチのコンテキストは、単に名前をコミットするだけではありません。あなたはそれについてのすべてをここで読むことができます。

ただし、一般的な考え方は、コーディングを開始する前にコミット名を記述することです。これにより、一度に1つのステップに集中でき、何をする必要があるかがより明確になり、暗黙的にコミットをアトミックにすることができます。これは非常に興味深いテイクであり、自分で試してみる計画だと思います。

パート2:レビュー担当者を割り当てる前

最初に覚えておくべきことは、レビュー担当者とは異なり、現在、タスクとソリューションのコンテキストに没頭しているということです。しかし、レビュー担当者がプルリクエストを開くと、レビュー担当者が持っているのはあなたが提供したものだけです。彼らはコードを書かず、問題に取り組むのにX時間も何日も費やしませんでした。したがって、レビューをより有用なものにするために、可能な限り多くのコンテキストを提供することが重要です。

3.コンテキストを提供する:説明と画像

説明

説明の作成について話すとき、JiraチケットをPRにリンクして、それで完了するという意味ではありません。これには、レビュー担当者がJiraに切り替えて説明を読む必要があります。これには、コードレビューに必要な情報よりも多くの情報が含まれている可能性がありますが、ソリューションの実装方法についてはわかりません。

簡単な説明を提供し、次の3つの質問に答えることで、これを回避できます。

  • 何?このプルリクエストによって実行されるタスクは何ですか?
  • どのように?それはどのように実装されていますか(ソリューションの概要)?
  • なんで?該当する場合(たとえば、いくつかの有効なアプローチが存在する場合)、なぜこのアプローチを選択したのですか、または他のアプローチの障害は何ですか?

この説明のおかげで、レビュー担当者はコードで何を探すべきか、そしてなぜそれがそのように書かれたのかを知ることができます。これにより、より関連性の高いコメントを提供できます。

これを確実にするには、GitHubにプルリクエストテンプレートを追加します。

画像と動画

実際の結果がどのように見えるかを知りながらコードを評価することは、想像するよりもはるかに簡単です。タスクがUIの変更に関連している場合、最終結果の画像(またはフローの場合はビデオ)を追加すると、レビュー担当者がコードを理解するのに非常に役立ちます。または、タスクに複雑なロジックが含まれる場合は、アルゴリズムを説明するシーケンス図を添付できます。

4.小さくしてください

プルリクエストが大きいほど、コードレビューは貧弱になります。コードレビューは精神的に難しいプロセスです。コードを読み、コードが何をしているのかを理解し、それがどのように行われているのかを理解し、潜在的な問題を探す必要があります。覚えておく必要のあるコード行が多いほど、何かを見落とす可能性が高くなります。実際には「黄金」の行数はありませんが、500未満に固執することをお勧めします。それ以上ある場合は、それらを複数のプルリクエストに分割します。

5.清潔に保つ

いいえ、ここではアーキテクチャについて話していません。TODO開発中は、印刷ログの生成、特定の値のハードコーディング、 sの残しなど、あらゆる種類のトリックを使用します。時間を節約し、レビュー担当者が手元の機能に集中できるようにするには、レビュー担当者を割り当てる前に、プルリクエストがクリーンであることを確認する習慣をつけてください。清潔に保つとはどういう意味ですか?

  1. すべてのデバッグログと未使用またはコメントアウトされたコードを見つけて削除します。
  2. TODO:をどうするかを決める
    1. それを実装します。
    2. すぐに実装できない場合は、タスクを作成するか、タスクがいつ実行されるかについて少なくとも何らかの見積もりを残してください。
    3. 完了したか、関連性がなくなった場合は削除してください。
  3. ハードコードされた文字列(可能なハードコードされた値のすべてではありませんが)に関して、私は最近、すべてのハードコードされた文字列に絵文字を追加することについてのVandad Nahavandipoorからのヒントに出くわしました(たとえば、拡張機能の助けを借りて)。

https://raw.githubusercontent.com/vandadnp/flutter-tips-and-tricks/main/images/hardcoded-strings-in-flutter.jpg#center

上級者向けのヒント:これらの問題のほとんどは静的コード分析ツールで解決できますが、処理方法によっては、カスタムルールの実装が必要なものもあります。

6.すべてのマージの競合を解決します

機能が完了し、コードが最終的なものであり、後でマージの競合を残すことができるため、これは些細なことのように思えるかもしれません…しかし、それが常に正しいとは限りません。ブランチがベースブランチから大きく分岐している可能性があります。または、一部の変更がコードと衝突して、リファクタリングなしでマージできない場合があります。そして、これには別のレビューが必要になるか、見落とされている可能性のある問題と統合されます。

7.code-genファイルを除外します

JSONモデルや単体テストモックなどのコード生成ファイルを確認する必要はありません。したがって、それらをdiffで明示的に表示しても、メリットはなく、単に煩わしいだけです。Gitクライアントが異なれば、そのようなファイルをレビューから除外するアプローチも異なります。たとえば、GitHubでは、パターンをに追加.gitattributesでき、最終的な差分で折りたたまれます。

https://pbs.twimg.com/media/FKwn0QDXEAUi_4q?format=jpg&name=large

8.静的コード分析

静的コード分析を有効にすることをお勧めします。Dartのドキュメントでは、その特典について非常に簡潔に説明しています。

静的分析を使用すると、1行のコードを実行する前に問題を見つけることができます。これは、バグを防ぎ、コードがスタイルガイドラインに準拠していることを確認するために使用される強力なツールです。

新しいDartおよびFlutterプロジェクトでは、デフォルトでlintルールが有効になっています。ここで、既存のプロジェクトでそれらを有効にする方法を読むことができます。ここでダートとここでフラッターのルールをチェックしてください。独自のルールを作成することも、既存のルールを無効にすることもできます。

レビューアを割り当てる前に、コードがlintルールに違反していないことを確認してください。CIを使用してこれを確認できます。lintの警告がなくなるまで、マージの可能性をブロックします。

プロのヒント: DartCodeMetricsなどのツールを使用してさらに先に進むことができます。

9.ユニットテスト

単体テストは、テストはもちろんのこと、他の誰かがコードを見る前に、開発の初期段階で問題を特定してバグを見つけるのに役立ちます。さらに、コードが変更された場合でもコードの動作が同じになるようにします。ボーナスとして、それらはまたあなたがよりクリーンでより分離されたコードを書くことを引き起こし、全体的なコード品質に貢献します。

すべてのプルリクエストで単体テストを実行し、何かが失敗した場合にマージをブロックするようにCIを設定できます。また、プルリクエストをレビューの準備ができているとマークする前に、失敗したテストを修正する必要があります。

プロのヒント:Codecovなどのツールを使用してコードカバレッジレポートを有効にします。

10.マイルストーンやラベルなどの他のマーカー

マイルストーンやラベルなどのマーカーを使用することは、より便利なヒントであり、プロジェクトのサイズによっては省略できます。ただし、大規模なプロジェクトでは、プルリクエストを整理するのに役立ちます。たとえば、確認するプルリクエストが多数ある場合は、マイルストーンでフィルタリングして、次のリリースのPRをより早く確認できます。また、ラベルは、バグ、機能、拡張機能などを示すことで、より多くのコンテキストを提供できます。

ボーナスチップ

他のレビューアを割り当てる前に、自分のコードをレビューしてください。はい、あなたはこのコードを書いたばかりで、まだすべてを覚えています。ただし、特にGitクライアントのGUIで見た目を新しくすることで、以前は見落としていた可能性のあるものを見つけることができます。

パート3:レビュー後

マージする前に、すべてのコメントに対処して解決してください。このようにして、何が実装され、何が実装されなかったかをだれにも推測させないようにします。また、アドレス指定とは、すべてのコメントを実装する必要があるという意味ではありませんが、少なくとも返信を残すか、絵文字に反応して、処理されたか処理されないかを示します。その後、レビュー担当者は会話を解決できます。GitHubには、すべての会話が解決されない限り、マージをブロックすることを有効にできる設定もあります。

その他のヒント

以下に、コードレビューと品質を改善するために実行できる他のことの非常に小さなリストを示します。

コードを書くときは、読んでみてください。読みながら多くの計算を行う必要がある場合は、リファクタリングを検討してください。外出先でコードを計算するということは、多くのものをメモリに保持することを意味し、人間の作業メモリは非常に限られています。これは、実行する必要のある計算が多いほど、全体像から何かを見逃す可能性が高くなり、コードレビューの効果が低下することを意味します。レビュー担当者として、進行中のすべてをメモリに保持するのが難しい場合は、コードをリファクタリングするように依頼します。(リファクタリングが必要なコードの例としては、複雑なブールチェックや長いメソッド本体があります。)

プルリクエストを開く前に、確認およびテストするチェックリストを用意してください。モバイルアプリの場合、これには、縦向きと横向き、OSの最小および最大APIレベル、フレーバー、テーマなどの構成を含めることができます。自分のチェックリストについての投稿があります。これは非常に古いものですが、依然として関連性があります。

GitHubには、ドラフトプルリクエストと呼ばれる機能があります。おそらく複雑な機能についての早期レビューが必要であるが、コードがまだレビューの準備ができていない場合は、プルリクエストをドラフトとしてマークできます。これは、PRの前に。などのタイトルを付けるよりも優れたソリューションだと思いますWIP

コード品質を制御するための他のツールを統合します。Flutter開発者の場合、これらはDart Code Metrics、 InvertaseのSpec、またはVeryGoodVenturesさまざまなツールである可能性があります。

パート4:優れたプルリクエストプラクティスを紹介するサンプルプロジェクト

次に、これらのヒントのいくつかを紹介するサンプルプロジェクトを設定しましょう。

  1. Codemagicを使用してマージする前に、静的コード分析と単体テストに合格する必要があります。
  2. Webhookを介してプルリクエストが変更されるたびにチェックを自動的に実行します。
  3. 新しいプルリクエストごとにプルリクエストテンプレートを使用します。
  4. また、プルリクエストの例をいくつか参照してください。

ステップ1:GitHubでFlutterプロジェクトを使用してリポジトリを作成する

https://github.com/darjaorlova/codemagic_pr_todo

ステップ2:プロジェクトをCodemagicに追加します

スクリーンショット2022-02-13、22.27.12.png#center

CodemagicにはFlutter用のワークフローエディターオプションがあり、かなり直感的なGUIを使用してFlutterプロジェクト用の本格的なCI/CDパイプラインを非常に簡単にセットアップできます。したがって、簡単にするために、ここではCodemagicのワークフローエディタを使用します。

ステップ3:プルリクエストのワークフローを作成する

Codemagicで、[ワークフローの複製]をクリックします。

スクリーンショット2022-02-13(22.28.36.png#center)

ワークフロー名をクリックして、に変更しPull requestます。

スクリーンショット2022-02-13(22.30.17.png#center)

ステップ4:テストのみを実行するようにワークフローを構成する

Codemagicのワークフローエディタで、[テストのみを実行]チェックボックスを選択します。

スクリーンショット2022-02-13(22.31.03.png#center)

[ビルドトリガー]タブを展開し、次のチェックボックスをオンにします。

  • プッシュでトリガー
  • プルリクエストの更新時にトリガー
  • 古いWebhookビルドをキャンセルする

[テスト]タブを展開し、[テストまたは分析が失敗した場合はビルドを停止する]チェックボックスがオンになっていることを確認します。

スクリーンショット2022-02-13(22.32.02.png#center)

[静的コード分析]タブの[フラッターアナライザーを有効にする]チェックボックスをオンにします。

スクリーンショット2022-02-13(22.33.53.png#center)

[統合と単体テスト]タブの[フラッターテストを有効にする]チェックボックスをオンにします。

スクリーンショット2022-02-13(22.34.45.png#center)

ワークフローの設定は以上です。変更を保存することを忘れないでください!

スクリーンショット2022-02-13(22.35.39.png#center)

最初のビルドを開始して、動作することを確認しましょう。

スクリーンショット2022-02-13(22.48.48.png#center)

ステップ5:GitHubでブランチルールを構成する

重要:ブランチ保護ルールは、GitHubProまたはTeamsアカウントをお持ちの場合にのみ設定できます。

[設定] → [ブランチ] → [ルールの追加]を開きます。

スクリーンショット2022-02-13(22.39.07.png#center)

必要なすべてのチェックボックスをオンにします。私たちの場合、それらは次のとおりです。

  • マージする前にプルリクエストを要求する
  • 承認が必要:1
  • マージする前にステータスチェックに合格する必要があります
  • マージする前にブランチを最新にする必要があります
  • マージする前に会話の解決が必要

ブランチ名を入力して保存します。

スクリーンショット2022-02-13(22.43.02.png#center)

この時点で、コードベースは基本的なFlutterカウンターアプリの例で構成されています。統合をテストしてみましょう:

  1. 接頭辞が。のブランチを作成しますtest_codemagic_integration。(ここでtestは、機能を導入せずに何かをテストしているブランチに使用するプレフィックスです。)
  2. 未使用のインポートまたはlint警告を発生させるその他のものを追加してプッシュします。
  3. プルリクエストを作成します。チェックが失敗したことを確認する必要があります。

これはPRです:https ://github.com/darjaorlova/codemagic_pr_todo/pull/1

スクリーンショット2022-02-13(22.58.23.png#center)

また、Codemagicコンソールで確認すると、ビルドが失敗したことがわかります。

スクリーンショット2022-02-13(22.59.11.png#center)

ビルドの詳細で、Flutteranalyzeが問題を発見したことがわかります。

スクリーンショット2022-02-13at23.00.30.png#center

現時点では、いくつか質問があるかもしれません。Codemagicは、ワークフローを実行するタイミングをどのように認識し、GitHubにどのようにレポートしましたか。答えはwebhook経由です。プロジェクトをGitHubに接続すると、Webhookが自動的に追加され、最初のステップで、そのWebhookのトリガーを設定しました。

Codemagicプロジェクトの設定で確認できます。

スクリーンショット2022-02-13(23.03.15.png#center)

そしてGitHubで:設定Webhook

スクリーンショット2022-02-13(23.05.00.png#center)

これは、ブランチ保護ルールで必要に応じて設定したステータスチェックです。

ステップ6:GitHubPRテンプレートを追加する

  1. プロジェクトのルートフォルダに、というファイルを追加する必要がありますpull_request_template.md
  2. 記入します(コミット、プッシュ、マージmain)。
## Description1. What type of code this is (e.g., bug fix, new feature)2. What it does and how it does it3. Why it is done that way (if required)4. Nice to have: images/videos (if it involves the UI) ## Checklist- [ ] Has unit tests- [ ] Coverage is at least 90%- [ ] Has documentation - [ ] Has release milestone

次回プルリクエストを開くと、すでに事前に入力されています。

スクリーンショット2022-02-13(23.21.45.png#center)

結果は次のようになります:https ://github.com/darjaorlova/codemagic_pr_todo/pull/3

スクリーンショット2022-02-13(23.26.12.png#center)

結論

これで統合は完了です。あなたが何か新しいことを学んだことを願っています。プルリクエストをより明確でレビューしやすくするためのこれらのヒントは、開発者としての生活を簡素化するのに役立ちます。ハッピープルリクエスト!

このプロジェクトを複製して例を確認したい場合は、codemagic.yamlファイルを含むGitHubリポジトリをここから入手できます。 

このストーリーは、もともとhttps://blog.codemagic.io/10-tips-for-better-pull-requests-and-code-review/で公開されました

#code #review 

プルリクエストとコードレビューを改善するための10のヒント

iOSコードのパフォーマンスを向上させる方法:配列内の検索を減らす

配列を反復処理する時間計算量はO(n)です。許容できるようですね。コードのパフォーマンスへの影響を無視し、締め切り前にやるべき仕事がたくさんある場合は、より良いソリューションを考えることに力を入れないかもしれません。しかし、このスキルを使用することが自然な応答になると、コードにネストされたforループまたはネストされたフィルターを記述したことに気付かない可能性があります。これにより、時間計算量がO(n²)に増加し、パフォーマンスが低下する可能性が非常に高くなります。後で発行します。

aとaでの検索の時間計算量はO(1)であり、aでの検索の時間計算量はO(n)であるため、の代わりにSet、などの非順次コレクションタイプで検索してみてください。DictionaryArraySetdictionaryArray

この記事では、とを使用して速度を最適化する方法を紹介しSetますdictionary

例1:設定

例1には、すべてのメンバーのデータとプラチナメンバーのIDがあります。目標は、からプラチナメンバーの配列を導出することmembersですplatinumMemberIDs

let members: [Member]
let platinumMemberIDs:[Int]

最適化前

の時間計算量filter(_:)はO(n)であり、配列のインスタンスメソッドの時間計算量もcontains(_:)O(n)です。

members次のアプローチは、配列を反復処理します。各反復で、現在のメンバーのIDが含まれているplatinumMemberIDsかどうかを確認するために検索します。platinumMemberIDs

このアプローチの時間計算量はO(n²)です。

var platinumMembers = members.filter {
    platinumMemberIDs.contains($0.id)
}

最適化後

順序は重要でplatinumMemberIDsはないため、このデータを非シーケンシャルコレクションタイプに保持できSetます。aでの検索の時間計算量SetはO(1)です。全体的な時間計算量はO(n)に減少します。

let platinumMemberIDSet = Set(platinumMemberIDs)var platinumMembers = members.filter {
    platinumMemberIDSet.contains($0.id)
}

例2:辞書

例2では、​​異なるソースからの2つのユーザーグループがあり、それらの共通部分を調べたいと思います。ユーザーの電子メールが他のグループの別のユーザーと同じである限り、それらは同じユーザーであると見なされます。

let starUsers: [User]
let recentUsers: [User]

最適化前

最も簡単な方法は、ネストされたforループを記述して重複したユーザーを見つけることですが、時間計算量はO(n²)であり、あまり理想的ではありません。

var duplicatedUsers = [User]()for star in starUsers {
  for recent in recentUsers {
    if star.email == recent.email {
      duplicatedUsers.append(star)
    }
  }
}

最適化後

recentUsers時間の複雑さを軽減するために、各ユーザーの電子メールをキーとして、ユーザーオブジェクトを値として、配列から辞書を作成するだけです。

let recentUserMap = Dictionary(uniqueKeysWithValues: recentUsers.map { ($0.email, $0) })

次に、配列を検索しますが、各反復で、最適化後に重複したユーザーを見つけるために配列をstarUsers反復処理する必要はありません。recentUsers代わりに、から直接、現在のスターユーザーと同じメールアドレスを持つ最近のユーザーのユーザーオブジェクトにアクセスしますrecentUserMap。時間計算量はO(n)に減少します。

let duplicatedUsers = starUsers.filter {
  guard let _ = recentUserMap[$0.email] else { return false }
  return true
}

このストーリーは、もともとhttps://betterprogramming.pub/how-to-boost-your-ios-code-performance-reduce-searching-an-array-55fbdfee2050で公開されました

#ios #code #swift 

iOSコードのパフォーマンスを向上させる方法:配列内の検索を減らす