AI Skills Every Developer Needs in 2026
The AI tooling landscape has settled enough that the question is no longer "should I learn this" but "which parts actually matter for my work." This post is my honest breakdown — what's worth investing time in, what's overhyped, and what separates engineers who ship AI products from those who build demos.
The Core Skill Map
| Skill Area | Priority | Why It Matters |
|---|---|---|
| Prompt engineering | High | Affects output quality on everything you build |
| Embeddings & RAG | High | Most real AI products need external knowledge |
| Tool use / Agents | High | Unlocks automation beyond chat |
| Evals & testing | High | The only way to ship AI reliably |
| Fine-tuning | Medium | Useful for specific domains, costly to maintain |
| Model architecture | Low | Unless you're at a lab, it's academic |
1. Prompt Engineering
This sounds basic but most developers underinvest here. The difference between a prompt that works and one that's production-ready is significant.
What actually works:
# ❌ Vague
prompt = "Summarize this document."
# ✅ Specific, with constraints and format
prompt = """
Summarize the following document in 3 bullet points.
- Each bullet must be under 20 words
- Focus on actionable insights, not background
- If the document is too short to summarize meaningfully, say "N/A"
Document:
{document}
"""Chain-of-thought for complex tasks:
prompt = """
You are analyzing a customer support ticket.
First, identify the problem type (bug / feature request / billing / other).
Then, assess urgency (high / medium / low) based on the user's language.
Finally, suggest the appropriate team to route it to.
Think through each step before giving your final answer.
Ticket: {ticket}
"""Structured output:
Most LLMs support JSON mode or structured output now. Use it — parsing free-form text is fragile:
from anthropic import Anthropic
import json
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
system="You always respond with valid JSON only, no prose.",
messages=[{
"role": "user",
"content": f"Extract: name, email, company from: {text}"
}]
)
data = json.loads(response.content[0].text)2. Embeddings and RAG
Retrieval-Augmented Generation is how you give an LLM access to your data without fine-tuning. The architecture is simple:
- Chunk and embed your documents
- Store embeddings in a vector database
- At query time, embed the question and retrieve similar chunks
- Pass retrieved chunks + question to the LLM
from openai import OpenAI
import numpy as np
client = OpenAI()
def embed(text: str) -> list[float]:
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# Index your docs
docs = ["FastAPI handles async by default.", "Django uses sync views by default."]
doc_embeddings = [embed(d) for d in docs]
# Query
query = "Which framework is better for async APIs?"
query_embedding = embed(query)
scores = [cosine_similarity(query_embedding, de) for de in doc_embeddings]
best = docs[np.argmax(scores)]
print(f"Most relevant: {best}")For production, use a real vector DB:
- pgvector — if you're already on PostgreSQL, lowest overhead
- Pinecone — managed, scales well, good SDK
- Qdrant — open source, strong filtering support
- Weaviate — good for hybrid search (vector + keyword)
Chunking strategy matters more than the model:
def chunk_document(text: str, size: int = 512, overlap: int = 64) -> list[str]:
words = text.split()
chunks = []
i = 0
while i < len(words):
chunk = " ".join(words[i:i + size])
chunks.append(chunk)
i += size - overlap
return chunksOverlap prevents important context from being split across chunk boundaries.
3. Tool Use and Agents
An agent is a loop: the model decides what to do, calls a tool, receives the result, and continues until the task is done.
def run_agent(task: str, tools: list, max_steps: int = 10):
messages = [{"role": "user", "content": task}]
for _ in range(max_steps):
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=2048,
tools=tools,
messages=messages
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason == "end_turn":
return response.content[0].text
if response.stop_reason == "tool_use":
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
})
messages.append({"role": "user", "content": tool_results})
return "Max steps reached"Keep tools small and well-named. One tool that does three things is harder for the model to reason about than three tools with clear purposes.
4. Evals — The Skill Nobody Talks About
This is the biggest differentiator between AI demos and AI products. Without evals, you're shipping blind.
A basic eval harness:
import json
from typing import Callable
def run_eval(
test_cases: list[dict],
fn: Callable,
scorer: Callable
) -> dict:
results = []
for case in test_cases:
output = fn(case["input"])
score = scorer(output, case["expected"])
results.append({
"input": case["input"],
"output": output,
"expected": case["expected"],
"score": score,
"passed": score >= 0.8
})
passed = sum(1 for r in results if r["passed"])
return {
"total": len(results),
"passed": passed,
"pass_rate": passed / len(results),
"results": results
}
# Example test cases for a summarizer
test_cases = [
{
"input": "FastAPI is a modern Python web framework...",
"expected": "Framework for building Python APIs"
},
# ...
]
report = run_eval(test_cases, my_summarizer, semantic_similarity_scorer)
print(f"Pass rate: {report['pass_rate']:.0%}")Types of evals to build:
| Eval Type | What It Tests |
|---|---|
| Unit evals | Single input → expected output |
| Regression evals | New model/prompt didn't break old cases |
| Human evals | Preference between two outputs |
| Red-team evals | Adversarial inputs, safety, jailbreaks |
Run evals before every prompt change. They're not tests for the LLM — they're tests for your system.
5. Context Management
LLMs have context windows but long contexts get expensive and slower. Manage what goes in:
def build_context(
system: str,
history: list[dict],
user_message: str,
max_tokens: int = 4000
) -> list[dict]:
# Estimate tokens (rough: 1 token ≈ 4 chars)
budget = max_tokens
budget -= len(system) // 4
budget -= len(user_message) // 4
# Include recent history that fits
included = []
for msg in reversed(history):
cost = len(msg["content"]) // 4
if budget - cost < 0:
break
included.insert(0, msg)
budget -= cost
return includedFor long-running sessions, summarize old turns instead of truncating them:
summary = client.messages.create(
model="claude-haiku-4-5-20251001", # cheap model for summarization
max_tokens=256,
messages=[{
"role": "user",
"content": f"Summarize this conversation in 3 sentences:\n{old_history}"
}]
).content[0].text6. MCP — Model Context Protocol
MCP is the emerging standard for giving LLMs access to tools and data sources. If you're building internal tooling or integrations, it's worth understanding now.
# MCP server (Python)
from mcp.server import Server
from mcp.server.models import InitializationOptions
server = Server("my-tools")
@server.list_tools()
async def list_tools():
return [
Tool(name="search_db", description="Search the internal database")
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "search_db":
return search_internal_db(arguments["query"])MCP servers can be connected to Claude Desktop, Cursor, or any MCP-compatible client — without writing custom integrations for each.
What to Focus On First
If you're just getting started with AI engineering, the order that gives the fastest ROI:
- Prompt engineering — immediate impact on everything
- Structured output + basic evals — makes your work reliable
- RAG with pgvector — covers 80% of "give the LLM my data" use cases
- Tool use / agents — once you have reliable primitives
- MCP — when you're building integrations or internal tooling
Skip fine-tuning until you've exhausted what good prompting + RAG can do. It's expensive to run and expensive to maintain.
The engineers who ship the best AI products aren't the ones who understand transformers — they're the ones who understand how to test, iterate, and fail fast.