або покрокова інструкція до того, як врівноважувати
здоровий ґлузд та перфекціонізм при рефакторингу
Усі приклади коду в доповіді висмоктані з пальця і можуть не відображати реальних проблем, а самі розглянуті проблеми притягнуті за вуха. Принципи й підходи, розглянуті в доповіді, не є вичерпним списком, а обрані для ілюстрації основних ідей, що я їх намагатимусь вам пояснити з перемінним успіхом. Меми взагалі вставлені аби було, а не тому шо смішно і підходять. Ще мені сказали, шо не можна тепер матюкатись. Срав пес (. І кажуть, що не треба більше зі сцени казати, що я доробляв слайди вночі в готельному номері після препаті. Тому я вам не скажу, де, коли і в якому стані доробляв слайди. Усі думки, висловлені під час доповіді, є виключно моїм субʼєктивним сприйняттям реальності і не можуть розглядатися як абсолютна істина, а навпаки мають бути піддані сумніву в дискусіях після виступу.
Переписування | Рефакторинг |
---|---|
Усунення технічного боргу раз і назавжди. І створення нового, чого вже. | Поступове усунення технічного боргу без breaking changes та з небагато меншими затратами часу на ітерацію. |
Використання сучасних технологій. | Осучаснення кодової бази відбувається так само ітеративно, без переривання процесу розробки. |
Покращення продуктивності та оптимізація архітектури. | Покращення продуктивності та оптимізація архітектури. Тільки потроху. |
import React, { useState, useMemo, useCallback } from "react";
const Dashboard = () => {
const [users, setUsers] = useState([]);
const [orders, setOrders] = useState([]);
const [formErrors, setFormErrors] = useState({});
const currencyFilter = "USD";
const fetchUsers = async () => {
const data = await fetch("/api/users").then((res) => res.json());
setUsers(data);
};
const fetchOrders = async () => {
const data = await fetch("/api/orders").then((res) => res.json());
setOrders(data);
};
const activeUsers = users.filter((user) => user.status === "active");
const activeOrders = orders.filter(
(order) => order.status === "active" && order.currency === currencyFilter
);
const validateOrder = useCallback((order) => {
if (order.amount <= 0) {
setFormErrors((prev) => ({ ...prev, [order.id]: "Invalid amount" }));
} else {
setFormErrors((prev) => ({ ...prev, [order.id]: "" }));
}
}, []);
const handleValidation = useCallback(() => {
activeOrders.forEach(validateOrder);
}, [activeOrders, validateOrder]);
const orderSummary = useMemo(
() =>
activeOrders.length > 0
? activeOrders.length > 10
? activeOrders.every((order) => order.priority === "high")
? activeOrders.some((order) => order.currency === "USD")
? "Many high-priority USD orders"
: "Many high-priority non-USD orders"
: "Many orders with mixed priorities"
: activeOrders.length > 5
? "Few active orders"
: "Very few orders"
: "No active orders",
[activeOrders]
);
return (
<div>
<button onClick={fetchUsers}>Load Users</button>
<button onClick={fetchOrders}>Load Orders</button>
<button onClick={handleValidation}>Validate Orders</button>
<h2>Users</h2>
<ul>
{activeUsers.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
<h2>Orders</h2>
<ul>
{activeOrders.map((order) => (
<li key={order.id}>
{order.product} - {order.currency}
{formErrors[order.id] && <span> - {formErrors[order.id]}</span>}
</li>
))}
</ul>
<p>{orderSummary}</p>
</div>
);
};
export default Dashboard;
DRY — Don’t Repeat Yourself
const Dashboard = () => {
…
const activeUsers = users
.filter(user => user.status === 'active');
const activeOrders = orders
.filter(order => order.status === 'active');
…
}
function useFilterData(data, status) {
return data.filter((item) => item.status === status);
}
const activeUsers = useFilterData(users, "active");
const activeOrders = useFilterData(orders, "active");
const useFilterData = (data, type, status, currency) => {
if (type === 'order') {
data = data.map(order => ({
...order,
currency: order.currency === 'USD' ? 'US Dollar' : order.currency,
}));
}
return data.filter(item => {
if (type === 'order') {
return item.status === status && item.currency === currency;
}
return item.status === status;
});
};
…
const curr = 'US Dollar';
const activeUsers = useFilter(users, 'user', 'active');
const activeOrders = useFilter(orders, 'order', 'active', curr);
Ну а згодом? Про наслідки можливі не подумать? Один лиш тільки раз і вже крадеться піздець з своєю усмішкою хижой
Але одна
const fetchUsers = async () => {
const data = await fetch("/api/users").then((res) => res.json());
setUsers(data);
};
const fetchOrders = async () => {
const data = await fetch("/api/orders").then((res) => res.json());
setOrders(data);
};
…
const [formErrors, setFormErrors] = useState({});
const validateOrder = useCallback((order) => {
if (order.amount <= 0) {
setFormErrors((prev) => ({ ...prev, [order.id]: "Invalid amount" }));
} else {
setFormErrors((prev) => ({ ...prev, [order.id]: "" }));
}
}, []);
import { useUsers } from "./useUsers";
import { useOrders } from "./useOrders";
import { validateOrder } from "./validation";
import { useFormErrors } from "./useFormErrors";
const Dashboard = () => {
const { users, fetchUsers } = useUsers();
const { orders, fetchOrders } = useOrders();
const { errors, validate } = useFormErrors();
const handleValidation = (order) => {
activeOrders.forEach(validate);
};
…
}
Народна мудрість. Не про KISS
const orderSummary = useMemo(
() =>
activeOrders.length > 0
? activeOrders.length > 10
? activeOrders.every((order) => order.priority === "high")
? activeOrders.some((order) => order.currency === "USD")
? "Many high-priority USD orders"
: "Many high-priority non-USD orders"
: "Many orders with mixed priorities"
: activeOrders.length > 5
? "Few active orders"
: "Very few orders"
: "No active orders",
[activeOrders]
);
const orderSummary = useMemo(() => {
if (activeOrders.length === 0) {
return "No active orders";
}
if (activeOrders.length > 10) {
const allHighPriority = activeOrders.every(
(order) => order.priority === "high"
);
const hasUsdOrders = activeOrders.some((order) => order.currency === "USD");
if (allHighPriority) {
if (hasUsdOrders) {
return "Many high-priority USD orders";
} else {
return "Many high-priority non-USD orders";
}
} else {
return "Many orders with mixed priorities";
}
}
if (activeOrders.length > 5) {
return "Few active orders";
}
return "Very few orders";
}, [activeOrders]);
Балансуючи ці три принципи, важливо враховувати контекст проєкту та конкретні вимоги. Іноді варто порушити один із принципів, щоб забезпечити простоту чи гнучкість, не жертвуючи зручністю підтримки.
Домашнє завдання: спробуйте відрефакторити код з цієї презентації, але обовʼязково прислухайтеся до свого внутрішнього маленького януковича
Не то шоб це прямо по темі доповіді, але цікаво і корисно буде.