メインコンテンツまでスキップ

LangChain

· 約5分
Mikyan
白い柴犬

LangChain: a framework for building applications powered by language models, provides:

  • Composability: Build complex AI workflows by chaining simple components.
  • Abstraction: Common interfaces for different LLMs, vector stores, etc.
  • Memory: Maintain context across interactions.
  • Tools: Connect LLMs to external data and capabilities

Core Architecture

Models: wrappers around LLMs that provide a consistent interface:

import { ChatOpenAI } from "@langchain/openai";
import { ChatAnthropic } from "@langchain/anthropic";

// All models share the same interface
const openAIModel = new ChatOpenAI({
modelName: "gpt-4",
temperature: 0.7,
});

const anthropicModel = new ChatAnthropic({
modelName: "claude-3-sonnet",
temperature: 0.7,
});

// Same method works for both
const response = await openAIModel.invoke("Hello, how are you?");

Messages - Communication Format

LangChain uses a message-based system: because modern chat models (GPT-4, Claude) are designed to work with conversations, not just single prompts. This system mirrors how the models are actually trained and how their APIs work.

Messages are structured objects that represent individual turns in a conversations. Each message has:

  • A role
  • Content
  • Optional metadata

A prompt is a template that generates messages, a message factory.

import { HumanMessage, SystemMessage, AIMessage } from "@langchain/core/messages";

const messages = [
new SystemMessage("You are a helpful assistant"),
new HumanMessage("What's the weather like?"),
new AIMessage("I don't have access to real-time weather data..."),
];

Prompts - Template System

Dynamic prompts with variable substitution

import { ChatPromptTemplate } from "@langchain/core/prompts";

const prompt = ChatPromptTemplate.fromTemplate(
"You are a {role}. Answer this question: {question}"
);

const formattedPrompt = await prompt.format({
role: "scientist",
question: "What is photosynthesis?"
});
const chatPrompt = ChatPromptTemplate.fromMessages([
["system", "You are {role}. Always be {personality}."],
["human", "{query}"]
]);

// Generates multiple messages
const messages = await chatPrompt.formatMessages({
role: "a chef",
personality: "enthusiastic and encouraging",
query: "How do I make pasta?"
});
// Result:
// [
// SystemMessage { content: "You are a chef. Always be enthusiastic and encouraging." },
// HumanMessage { content: "How do I make pasta?" }
// ]

Chains - Composition Pattern: connect components in sequence

Output Parsers - Structure Results

import { z } from "zod";
import { StructuredOutputParser } from "langchain/output_parsers";

const parser = StructuredOutputParser.fromZodSchema(
z.object({
name: z.string(),
age: z.number(),
interests: z.array(z.string())
})
);

// Parser generates format instructions for the LLM
const formatInstructions = parser.getFormatInstructions();

Runnable: the foundational abstraction in LangChain, represents any component that can process input and produce output.

invoke() - Single Request, Complete Reponse

processes one input and returns the complete output after processing is done

  • you need the full response before proceeding
  • simple request-repsonse patterns
  • when you don't need real-time feedback

stream() - Single Request, Streaming Response

Use when:

  • Real-time UI updates
  • Large responses that take time
  • Better user experience with immediate feedback
  • Processing data as it arrives
const model = new ChatOpenAI();

// Streaming response - chunks arrive as generated
const stream = await model.stream("Write a long story about space");

for await (const chunk of stream) {
process.stdout.write(chunk.content); // Print each chunk as it arrives
}

batch() - multiple Requests, parallel processing

  • processing multiple items efficiently
  • bulk operations
  • when you have rate limits but want parallelism
  • analyzing multiple documents simultaneously

Runnables can be chained using .pipe():

const chain = prompt
.pipe(model)
.pipe(outputParser)
.pipe(customProcessor);

// All three methods work on chains
await chain.invoke(input);
await chain.stream(input);
await chain.batch(inputs);

Everything in LangChain implements the Runnable interface

interface Runnable {
invoke(input: any): Promise<any>;
stream(input: any): AsyncGenerator<any>;
batch(inputs: any[]): Promise<any[]>;
}

Work together

import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";

// 1. Create components
const model = new ChatOpenAI({ temperature: 0 });
const prompt = ChatPromptTemplate.fromTemplate(
"Tell me a joke about {topic}"
);
const outputParser = new StringOutputParser();

// 2. Chain them together
const chain = prompt.pipe(model).pipe(outputParser);

// 3. Run the chain
const joke = await chain.invoke({ topic: "programming" });
// Input → Prompt → Model → Parser → Output

Memory system maintains conversation context:

Memory in LangChain is both the messages, and additional context / summaries.

Memory = Conversation History + State Management:

Memory in LangChain is a system that:

  • Stores the conversation history (messages)
  • Manages how that history is used
  • Can transform/summarize the history
  • Decides what to remember and what to forget

Types of Memory:

  • BufferMemory: Stores Everything
  • BufferWindowMemory - Limited History
  • ConversationSummaryMemory - Compressed History
  • ConversationSummaryBufferMemory - Hybrid Approach
import { BufferMemory } from "langchain/memory";
import { ConversationChain } from "langchain/chains";

const memory = new BufferMemory();
const chain = new ConversationChain({
llm: model,
memory: memory,
});

// First interaction
await chain.call({ input: "My name is Alice" });
// Second interaction remembers the first
await chain.call({ input: "What's my name?" }); // Knows it's Alice

Document Processing Pipeline: For RAG (Retrieval Augmented Generation):

const loader = new TextLoader("./document.txt");
const docs = await loader.load();

// 2. Split into chunks
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const chunks = await splitter.splitDocuments(docs);

// 3. Create embeddings and store
const embeddings = new OpenAIEmbeddings();
const vectorStore = await MemoryVectorStore.fromDocuments(
chunks,
embeddings
);

// 4. Create retrieval chain
const retriever = vectorStore.asRetriever();
const chain = RetrievalQAChain.fromLLM(model, retriever);

Agents - Dynamic Decision Making

import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { Calculator } from "langchain/tools/calculator";

const tools = [new Calculator()];

const agent = await initializeAgentExecutorWithOptions(tools, model, {
agentType: "zero-shot-react-description",
});

const result = await agent.call({
input: "What is 25% of 180?"
});
// Agent decides to use calculator tool

ffmpeg cheat sheet

· 約1分
Mikyan
白い柴犬
# Transform to 3 times play
ffmpeg -i input.mp4 -vf setpts=PTS/3 -af atempo=3 output.mp4

Gitlab CI, Zero to Hero

· 約6分
Mikyan
白い柴犬

.gitlab-ci.yml file is the entry point of GitLab CI/CD, must be placed in your repository root.

Job, Stage, Pipeline

Job: Individual tasks that run commands, build blocks of Gitlab CI pipeline.

Pipeline: The top-level component containing all jobs. However there is no way to directly define a pipeline, user can only define jobs, Gitlab constructs pipeline on:

  • Which jobs match the trigger conditions
  • How those jobs are related

Common Patterns for pipeline separation:

  • by branch
  • by pipeline source
  • by variables

Stage: groups of jobs that run in sequence

Stages are a grouping mechanism for jobs They define execution sequence

GitLab have a default stages definition. Or you can define stages in .gitlab-ci.yml. GitLab just enforces the execution order you specify.

# If you omit the stages: declaration
# GitLab uses these DEFAULT stages:
# - .pre
# - build
# - test
# - deploy
# - .post

stages:
- prepare
- build
- test
- deploy

GitLab Runners

Gitlab runner is a software (agent) that execute your CI/CD jobs

Listener/worker, that constantly polls GitLab for jobs, execute job, report result.

Gitlab Runners are installed on VM/container/server/kubernetes Pod

Type of runners:

  • Shared Runners: Available to all projects
  • Group Runners: Available to all projects in a group
  • Specific Runners: Dedicated to specific projects

Runners use Executors to run your job.

Executor Types:

  • shell-job
  • docker-job
  • k8s-job

default is docker executor

each job runs in a brand new container. for the following reasons:

  • isolation
  • predictability
Pipeline Start

[build-job]
├─ Pull node:16 image
├─ Create new container
├─ Run scripts
└─ Destroy container ❌

[test-job]
├─ Pull node:16 image (cached)
├─ Create NEW container
├─ Run scripts (no files from build!)
└─ Destroy container ❌

Pipeline End

Sharing data between jobs: artifacts

Gitlabe automatically clones your repository before any job.

stages:
- install
- build
- test
- deploy

install-deps:
stage: install
image: node:16
script:
- npm ci
- npm audit
artifacts:
paths:
- node_modules/
expire_in: 1 day

build-app:
stage: build
image: node:16
script:
- npm run build
dependencies:
- install-deps # Gets node_modules/
artifacts:
paths:
- dist/
- node_modules/

test-unit:
stage: test
image: node:16 # Fresh container!
script:
- npm test
dependencies:
- build-app # Gets dist/ and node_modules/

test-e2e:
stage: test
image: cypress/included:10 # Different image, fresh container
services:
- name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
alias: app
script:
- cypress run --config baseUrl=http://app:3000
dependencies:
- build-app

deploy:
stage: deploy
image: alpine:latest # Fresh container
script:
- apk add --no-cache curl
- curl -X POST $DEPLOY_WEBHOOK
dependencies:
- build-app # Only needs dist/

Cache: Speed optimization (might exist, might not)

Artifacts: guaranteed data transfer between jobs

If Job B needs Job A → B gets A's artifacts If Job B doesn't reference A → B can NEVER get A's artifacts

Cache is a way to preserve files between pipeline runs to speed up the jobs. cache is about preserving data across different pipelines.

Basic Job Configuration

Script types:

  • script: the only required field in a job. containing main job logic
    • commands run in sequence
    • if any command fails, job fails
    • runs in the project directory
    • has access to all CI variables
  • before_script: run before the main script, Perfect for environment setup
  • after_script: run after the main script, even if the job fails

why we need three scripts:

  • separation of concerns
  • guaranteed cleanup
  • error handling simplicity

Configurations control when jobs run

  • only/except: legacy, but still in use, specify the branch

  • when: specify the execution timing, can be used standalone or within rules

    • on_success: run if all previous succeeded
  • rules (recomended):

    • changes: File Changes:
    • exists: File existence
    • allow_failure: in rules
    • variables: Set variables in rules
  • trigger: use to start a completely separate pipeline. either in the same project or a different project

job:
rules:
# Branch conditions
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH != "main"'
- if: '$CI_COMMIT_BRANCH =~ /^feature-/'

# Tag conditions
- if: '$CI_COMMIT_TAG'
- if: '$CI_COMMIT_TAG =~ /^v\d+/'

# Pipeline source
- if: '$CI_PIPELINE_SOURCE == "push"'
- if: '$CI_PIPELINE_SOURCE == "web"'
- if: '$CI_PIPELINE_SOURCE == "schedule"'

# Complex conditions
- if: '$CI_COMMIT_BRANCH == "main" && $DEPLOY_ENABLED == "true"'
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main" || $CI_MERGE_REQUEST_LABELS =~ /urgent/'

Keywords:

stage: image: specify which docker image to use services: Helper containers variables: environment variables artifacts: save job output dependencies: control artifact flow needs: DAG (Directed Acyclic Graph) rules: smart conditions (better than only / except) when: execution conditions cache: speed up pipelines retry: handle flaky jobs timeout: prevent hanging allow_failure: non-blocking failures coverage: extract coverage environment: track deployments parallel: multiple instances trigger: pipeline control only

tags

Job Status Types:

pending/running/success/failed/skipped/manual

failed: script returned non-zero exit code skipped means the didn't meet conditions manual: waiting for manual trigger

CI/CD variables:

key-value pairs available during job execution. Environment variables on steroids

  1. Predefined variables (GitLab Provides)
  • CI_PROJECT_NAME
  • CI_COMMIT_BRANCH
  • CI_COMMIT_SHA
  • CI_COMMIT_MESSAGE
  • CI_PIPELINE_ID
  • CI_PIPELINE_SOURCE
  • CI_JOB_ID
  • CI_JOB_NAME
  • CI_RUNNER_ID
  • CI_RUNNER_TAGS
  • CI_PROJECT_URL
  • CI_PIPELINE_URL
  • CI_JOB_URL
deploy:
script:
# Different behavior for different triggers
- |
if [ "$CI_PIPELINE_SOURCE" == "merge_request_event" ]; then
echo "This is a merge request"
fi

# Use commit info
- docker build -t myapp:$CI_COMMIT_SHORT_SHA .

# Conditional logic
- |
if [ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]; then
echo "On default branch (usually main)"
fi

Variables can be defined at multiple levels, Higher level wins.

Job -> Pipeline -> Project -> Group -> Instance -> Predefined

  • protected variables
  • masked variables
  • file variables
  • variable expansion
build:
variables:
VERSION: "1.0.0"
IMAGE_TAG: "myapp:$VERSION" # Expands to myapp:1.0.0

Conditional variables

deploy:
script:
- echo "Deploying to $ENVIRONMENT"
rules:
- if: $CI_COMMIT_BRANCH == "main"
variables:
ENVIRONMENT: production
REPLICAS: "5"
- if: $CI_COMMIT_BRANCH == "develop"
variables:
ENVIRONMENT: staging
REPLICAS: "2"

Dynamic variables

build:
script:
# Create variables during job
- export BUILD_TIME=$(date +%Y%m%d-%H%M%S)
- echo "VERSION=$CI_COMMIT_SHORT_SHA-$BUILD_TIME" >> build.env
artifacts:
reports:
dotenv: build.env # Pass to next jobs

test:
needs: [build]
script:
- echo "Testing version: $VERSION" # From build.env

Needs

let you create DAG instead of rigid stage-based pipelines.

PostgreSQLのRow Level Security紹介

· 約1分
Mikyan
白い柴犬

PostgreSQLのRow Level Securityを使って、DBレベルで、権限制限できて、より安全なシステム開発できる。

例えば、UserProfileのセレクト、Updateクエリをuser.idで制限したら、他人の情報の誤操作を防げます。

Row Level Securityとは

名前通り、PostgreSQLデータベースのテーブルPolicyの設定によって、行ごとのR/W権限設定できる。

SupabaseはこのRLSを活用して、

https://www.youtube.com/watch?v=vZT1Qx2xUCo&t=691s

PostgreSQLのRow Level Security紹介

· 約1分
Mikyan
白い柴犬

PostgreSQLのRow Level Securityを使って、DBレベルで、権限制限できて、より安全なシステム開発できる。

例えば、UserProfileのセレクト、Updateクエリをuser.idで制限したら、他人の情報の誤操作を防げます。

Row Level Securityとは

名前通り、PostgreSQLデータベースのテーブルPolicyの設定によって、行ごとのR/W権限設定できる。

SupabaseはこのRLSを活用して、

https://www.youtube.com/watch?v=vZT1Qx2xUCo&t=691s

asdfでPythonをバージョン管理

· 約8分
Mikyan
白い柴犬

Pythonのバージョン管理にasdfを使用することで、複数のPythonバージョンを簡単に管理できるようになりました。

asdfはPythonだけでなく、Node.js、Javaなど複数のランタイムを統一的に管理できる非常に便利なツールです。

以下の手順はasdf v0.18.0に基づいて説明します。

asdfとは

asdfは複数のプログラミング言語のランタイムバージョンを管理するためのツールです。pyenv、nvm、rbenvなどの個別のバージョン管理ツールを統一的に置き換えることができます。

主な利点

  • 複数の言語を一つのツールで管理
  • プロジェクトごとのバージョン設定
  • 豊富なプラグインエコシステム

asdfとvenvの違い

asdf: Pythonインタープリター自体のバージョン管理

  • Python 3.11.7、3.12.1など異なるPythonバージョンを切り替え

venv: パッケージ(ライブラリ)の分離管理

  • 同じPythonバージョンでもプロジェクトごとに異なるパッケージセットを使用
  • 依存関係の競合を防ぐ

両方を組み合わせることで、Pythonバージョンとパッケージの両方を適切に管理できます。

asdfとvenvの違い

asdf: Pythonインタープリター自体のバージョン管理

  • Python 3.11.7、3.12.1など異なるPythonバージョンを切り替え

venv: パッケージ(ライブラリ)の分離管理

  • 同じPythonバージョンでもプロジェクトごとに異なるパッケージセットを使用
  • 依存関係の競合を防ぐ

両方を組み合わせることで、Pythonバージョンとパッケージの両方を適切に管理できます。

asdfのインストール

macOSの場合

公式ガイドに従ってインストールします。

brew install asdf

zshの設定

~/.zshrcファイルに以下を追加:

# asdfの設定
export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"
export ASDF_DATA_DIR="$HOME/.asdf/data"

補完機能の設定

# 補完ディレクトリの作成
mkdir -p "${ASDF_DATA_DIR:-$HOME/.asdf}/completions"

# zsh補完ファイルの生成
asdf completion zsh > "${ASDF_DATA_DIR:-$HOME/.asdf}/completions/_asdf"

再度~/.zshrcに以下を追加:

# asdf補完の有効化
fpath=(${ASDF_DATA_DIR:-$HOME/.asdf}/completions $fpath)
autoload -Uz compinit && compinit

設定を反映:

source ~/.zshrc

Pythonプラグインのインストールと設定

プラグインの追加

# Pythonプラグインを追加
asdf plugin add python

# インストール済みプラグインの確認
asdf plugin list

利用可能なPythonバージョンの確認

# インストール可能なPythonバージョンを表示
asdf list all python

Pythonのインストール

# 最新版をインストール
asdf install python latest

# 特定バージョンをインストール(例)
asdf install python 3.11.7

# インストール済みバージョンの確認
asdf list python

バージョンの設定

asdf v0.18.0ではasdf setコマンドを使用します:

# グローバル設定(HOMEフォルダ以下全体で使用)
asdf set --home python latest

# 現在の設定確認
asdf current python

出力例:

Name            Version         Source
python 3.13.5 /Users/username/.tool-versions
# Pythonコマンドの場所確認
which python3

出力例:

/Users/username/.asdf/data/shims/python3

プロジェクト固有の設定

プロジェクトディレクトリで特定のPythonバージョンを使用する場合:

# プロジェクトディレクトリに移動
cd /path/to/your/project

# プロジェクト用のPythonバージョンを設定
asdf set python 3.11.7

# .tool-versionsファイルが作成される
cat .tool-versions

出力例:

python 3.11.7

このファイルがあるディレクトリとその配下では、指定されたPythonバージョンが自動的に使用されます。

よく使うコマンド一覧

# 現在使用中のバージョン確認
asdf current

# 特定言語の現在バージョン
asdf current python

# インストール済みバージョン一覧
asdf list python

# 利用可能バージョン一覧(最新10件)
asdf list all python | tail -10

# プラグインの更新
asdf plugin update python


# 古いバージョンの削除
asdf uninstall python 3.10.0

トラブルシューティング

Pythonが見つからない場合

# shimの再作成
asdf reshim python

# パスの確認
echo $PATH | grep asdf

権限エラーが発生する場合

# asdfディレクトリの権限確認
ls -la ~/.asdf/

# 必要に応じて権限修正
chmod -R 755 ~/.asdf/

設定が反映されない場合

# .zshrcの再読み込み
source ~/.zshrc

# 現在のシェル設定確認
echo $ASDF_DATA_DIR
echo $PATH | grep asdf

実際の使用例

新しいプロジェクトでの設定

# プロジェクトディレクトリを作成
mkdir my-python-project
cd my-python-project

# プロジェクト用のPythonバージョンを指定
asdf set --local python 3.11.7

# 仮想環境の作成
python -m venv venv
source venv/bin/activate

# パッケージのインストール
pip install requests flask

チームでの.tool-versionsファイル共有

# .tool-versionsファイルをGitで共有
echo "python 3.11.7" > .tool-versions
echo "nodejs 18.19.0" >> .tool-versions

# チームメンバーは以下でバージョンをインストール
asdf install

設定が反映されない場合

# .zshrcの再読み込み
source ~/.zshrc

# 現在のシェル設定確認
echo $ASDF_DATA_DIR
echo $PATH | grep asdf

実際の使用例

新しいプロジェクトでの設定

# プロジェクトディレクトリを作成
mkdir my-python-project
cd my-python-project

# 1. asdfでPythonバージョンを指定(インタープリター管理)
asdf set python 3.11.7

# 2. venvで仮想環境を作成(パッケージ管理)
python -m venv venv
source venv/bin/activate

# 3. プロジェクト固有のパッケージをインストール
pip install requests flask

# 結果:Python 3.11.7 + 独立したパッケージ環境

なぜvenvも必要なのか?

問題:venvを使わない場合

# プロジェクトA
cd project-a
asdf set python 3.11.7
pip install django==4.2.0 requests==2.28.0

# プロジェクトB(同じPython 3.11.7)
cd ../project-b
pip install flask==2.3.0 requests==2.31.0 # ← requests 2.28.0が上書きされる

# プロジェクトAに戻ると...
cd ../project-a
# django が requests 2.31.0 で動作しない可能性!

解決:asdf + venv

# プロジェクトA: Python 3.11.7 + 独立した環境
project-a/ → django 4.2.0 + requests 2.28.0

# プロジェクトB: Python 3.11.7 + 独立した環境
project-b/ → flask 2.3.0 + requests 2.31.0

チームでの.tool-versionsファイル共有

# .tool-versionsファイルをGitで共有
echo "python 3.11.7" > .tool-versions
echo "nodejs 18.19.0" >> .tool-versions

# チームメンバーは以下でバージョンをインストール
asdf install

まとめ

asdfを使用することで以下のメリットが得られます:

  • 統一的な管理: 複数の言語のバージョン管理を一つのツールで実現
  • プロジェクト固有設定: .tool-versionsファイルでプロジェクトごとの環境を管理
  • 簡単な切り替え: コマンド一つでバージョン切り替えが可能
  • チーム開発: .tool-versionsファイルでチーム全体の環境を統一

Python開発での環境構築が大幅に簡素化されるため、特に複数のプロジェクトを扱う開発者にはぜひ活用していただきたいツールです。

ECR

· 約2分
Mikyan
白い柴犬

Amazon ECR は、フルマネージドのコンテナイメージレジストリサービスです。Docker ImageをPrivateで保存、管理できます。

アクセス制御:各リポジトリに対してリソースベースのIAMポリシーを設定し、プッシュ・プルの許可を細かくコントロールでいます。 保存:AWS S3上に保存されます。 ライフサイクル管理:自動削除ルールを定義可能。 脆弱性スキャン:プッシュ時に自動的に脆弱性スキャンを実行し、結果を取得できます。

ワークフロー

リポジトリの作成

aws ecr create-repository \
--repository-name my-org \
--region ap-northeast-1

認証&Dockerログイン

aws ecr get-login-password \
--region ap-northeast-1 \
--username AWS \
--password-stdin <account-id>.dkr.erc.ap-northeast-1.amazonaws.com

イメージのビルド、タグ付け、プッシュ

docker build -t myapp:v1 .
docker tag my-app:v1 \
<account-id>.dkr.erc.ap-northeast-1.amazonaws.com/my-app:v1
docker push <account-id>.dkr.erc.ap-northeast-1.amazonaws.com/my-app:v1

イメージのプル

docker pull <account-id>.dkr.erc.ap-northeast-1.amazonaws.com/my-app:v1

kubectlによるEKSにデプロイ

KubernetesマニフェストにそのデプロイしたイメージのURLを設定し、下記コマンドで適用したら簡単にEKSにデプロイできます。

# deploy
kubectl apply -f deployment.yaml

# status confirm
kubectl get pods -l app=my-app

Common Used Javascript Patterns for Screening Questions

· 約3分
Mikyan
白い柴犬

短時間で、LeetCode問題を解決するスクリーニング面接はあります。 そのスクリーニング面接で使われている書き方は効率を重視し、代わりに、readability をTradeoffしても。 プロジェクトコーディングと全く別物で、よく使っているパターンをここでまとめます。

計算

Javascript において、除算は小数ついているため、整除ははMath.truncで、小数部分を切り捨てましょう。 また、Math.floor()は正数を正しく処理できますが、負数の処理は数学上の整除定義と異なるので、使わないでください。

Math.trunc(3 / 2)  // → 1
Math.trunc(-3 / 2) // → -1

配列

配列の作成

const a = [];            // empty
const b = [1,2,3]; // with elements
const c = new Array(n); // [ <n × empty> ]
const d = Array(n).fill(0); // [0,0,…,0]
const e = Array.from({ length: n }, (_, i) => i*2);
const f = [...someOtherArray]; // shallow copy

注意しなければないこと: 以下はReference作成で、おかしい挙動になります。

const d = Array(n).fill([]);

その他の配列の操作:


// find the index
["hello", "world"].indexOf("world")

// sort, O(nlgn)
arr.sort((a, b) => a - b);

// reverse in place
arr.reverse()

// non destructive subarray
arr.slice(start, end)


// unshift
// output: 5 length of new array
// array1 to be: [10, 11, 1, 2, 3]
const array1 = [1, 2, 3];
array1.unshift(10,11)

使ってはいけない操作:

shift

// shift
// remove the first element and returns that removed element
// However the time complexity is O(n), so
const array1 = [1,2,3];
while (firstElement = array1.shift()) {
// ❌
}

代わりに headを使って、先頭の位置だけメモし、Queueの操作を実現しましょう。

const array1 = [1,2,3];
const head = 0;
while (firstElement = array1[head++]) {
// ✅
}

文字列

charを取得

'abcde'.charAt(2); // 'c'

React Error Boundary

· 約2分
Mikyan
白い柴犬

Error boundaries are React components that catch Javascript errors, in their child component tree, log those errors, and display a fallback UI instead of crashing the entire app. Without them, an error in one component can bring down your whole application.

The word "boundary" emphasizes that these components create a containment zone for errors:

Error Boundaries are specifically for:

React component lifecycle errors Render method errors Constructor errors in child components

How error boundaries works

How React Rendering works

React rendering happens in two phases:

// 1. Render Phase - React calls your components
function MyComponent() {
// If error happens here...
return <div>{undefined.toString()}</div>; // 💥 Crash!
}

// 2. Commit Phase - React updates the DOM
// (If render phase succeeds)

What Makes Component Errors Special

  1. They Happen Inside React's Control
// React controls this execution:
function BrokenComponent() {
const user = null;
return <h1>{user.name}</h1>; // React catches this error
}

// React DOESN'T control this:
<button onClick={() => {
throw new Error(); // Happens outside React's render cycle
}}>
  1. Synchronous Execution
// Error boundaries can catch this (synchronous):
function Component() {
throw new Error('Sync error');
return <div />;
}

// But NOT this (asynchronous):
function Component() {
setTimeout(() => {
throw new Error('Async error'); // Outside React's try/catch
}, 1000);
return <div />;
}

Why Error Boundaries work

Usage

✅ Use Error Boundaries For

  1. Unexpected Runtime Errors
  2. Third-Party Component Errors
  3. Dynamic/Complex Components

❌ DON'T Use Error Boundaries For

  1. Expected Errors (Handle These Explicitly)
  2. Event Handler Errors
  3. Async Errors

Best Practices

  1. Strategic Placement
function App() {
return (
<ErrorBoundary> {/* App-level boundary */}
<Header />
<ErrorBoundary> {/* Feature-level boundary */}
<RiskyFeature />
</ErrorBoundary>
<Footer />
</ErrorBoundary>
);
}
  1. Granular for Isolation
<div>
{widgets.map(widget => (
<ErrorBoundary key={widget.id}> {/* Each widget isolated */}
<Widget {...widget} />
</ErrorBoundary>
))}
</div>
  1. Not for Control Flow
// ❌ Bad: Using error boundary for expected cases
<ErrorBoundary fallback={<LoginForm />}>
<AuthenticatedApp />
</ErrorBoundary>

// ✅ Good: Explicit control flow
{isAuthenticated ? <AuthenticatedApp /> : <LoginForm />}