Refactoring yang baik dan refactoring yang buruk – Beragampengetahuan
Saya telah mempekerjakan banyak pengembang selama bertahun-tahun. Banyak dari mereka yakin bahwa kode kami memerlukan banyak pemfaktoran ulang. Namun masalahnya: di hampir semua kasus, pengembang lain merasa kode mereka yang baru difaktorkan ulang lebih sulit untuk dipahami dan dipelihara. Biasanya juga lebih lambat dan lebih rentan terhadap masalah.
Sekarang, jangan salah paham. Refactoring pada dasarnya bukanlah hal yang buruk. Ini adalah bagian penting untuk menjaga basis kode Anda tetap sehat. Masalahnya adalah refactoring yang buruk itu sangat buruk. Sungguh menakjubkan betapa mudahnya kita terjebak dalam membuat keadaan menjadi lebih buruk ketika mencoba memperbaikinya.
Jadi, mari kita lihat apa itu refactoring yang baik dan refactoring yang buruk, dan bagaimana menghindari menjadi pengembang yang ditakuti semua orang di basis kode Anda.

Contents
Yang baik, yang buruk, dan jelek dari refactoring
Abstraksi bisa menjadi hal yang baik. Abstraksi bisa berdampak buruk. Kuncinya adalah mengetahui kapan dan bagaimana menerapkannya. Mari kita lihat beberapa kendala umum dan cara menghindarinya.
1. Ubah gaya pengkodean Anda secara dramatis
Salah satu kesalahan paling umum yang saya lihat adalah ketika pengembang mengubah sepenuhnya gaya pengkodeannya selama proses pemfaktoran ulang. Hal ini biasanya terjadi ketika seseorang berasal dari latar belakang berbeda atau memiliki pendapat kuat tentang paradigma pemrograman tertentu.
Mari kita lihat sebuah contoh. Bayangkan kita memiliki sepotong kode yang perlu dibersihkan:
// this code could be cleaner
function processUsers(users: User[]) {
const result = [];
for (let i = 0; i < users.length; i++) {
if (users[i].age >= 18) {
const formattedUser = {
name: users[i].name.toUpperCase(),
age: users[i].age,
isAdult: true
};
result.push(formattedUser);
}
}
return result;
}
pemfaktoran ulang yang buruk
import * as R from 'ramda';
// adopted a completely different style + library
const processUsers = R.pipe(
R.filter(R.propSatisfies(R.gte(R.__, 18), 'age')),
R.map(R.applySpec({
name: R.pipe(R.prop('name'), R.toUpper),
age: R.prop('age'),
isAdult: R.always(true)
}))
);
Meskipun versi refactored ini mungkin menarik bagi penggemar pemrograman fungsional, versi ini memperkenalkan perpustakaan baru (Ramda) dan gaya pengkodean yang benar-benar berbeda. Bagi tim yang tidak terbiasa dengan pendekatan ini, mempertahankan pendekatan ini bisa menjadi mimpi buruk.
pemfaktoran ulang yang baik
// cleaner and more conventional
function processUsers(users: User[]): FormattedUser[] {
return users
.filter(user => user.age >= 18)
.map(user => ({
name: user.name.toUpperCase(),
age: user.age,
isAdult: true
}));
}
Versi ini bekerja dengan menggunakan pendekatan JavaScript yang lebih idiomatis seperti filter Dan map. Lebih bersih dan mudah dibaca, tetapi tidak memperkenalkan paradigma baru atau ketergantungan eksternal.
2. Abstraksi yang tidak perlu
Saya pernah mempekerjakan seorang pria yang menambahkan banyak abstraksi baru tanpa memahami kode yang mendasarinya. Mereka mulai mengelompokkan hal-hal yang tidak seharusnya dikelompokkan, dan lama kelamaan mereka menyimpang (dengan sengaja). Mereka menggabungkan beberapa konfigurasi yang tidak seharusnya (API yang berbeda memerlukan konfigurasi yang berbeda).
maju
// this code could be cleaner
function processUsers(users: User[]) {
const result = [];
for (let i = 0; i < users.length; i++) {
if (users[i].age >= 18) {
const formattedUser = {
name: users[i].name.toUpperCase(),
age: users[i].age,
isAdult: true
};
result.push(formattedUser);
}
}
return result;
}
pemfaktoran ulang yang buruk
// there are way more layers and abstractions here than necessary
class UserProcessor {
private users: User[];
constructor(users: User[]) {
this.users = users;
}
public process(): FormattedUser[] {
return this.filterAdults().formatUsers();
}
private filterAdults(): UserProcessor {
this.users = this.users.filter(user => user.age >= 18);
return this;
}
private formatUsers(): FormattedUser[] {
return this.users.map(user => ({
name: this.formatName(user.name),
age: user.age,
isAdult: true
}));
}
private formatName(name: string): string {
return name.toUpperCase();
}
}
const processUsers = (users: User[]): FormattedUser[] => {
return new UserProcessor(users).process();
};
Pemfaktoran ulang ini memperkenalkan kelas dengan beberapa metode, yang mungkin tampak lebih “berorientasi objek”, namun sebenarnya lebih kompleks dan sulit dipahami pada pandangan pertama.
pemfaktoran ulang yang baik
// cleaner and more conventional
const isAdult = (user: User): boolean => user.age >= 18;
const formatUser = (user: User): FormattedUser => ({
name: user.name.toUpperCase(),
age: user.age,
isAdult: true
});
function processUsers(users: User[]): FormattedUser[] {
return users.filter(isAdult).map(formatUser);
}
Versi ini memecah logika menjadi fungsi-fungsi kecil yang dapat digunakan kembali tanpa menimbulkan kerumitan yang tidak perlu.
3. Tambahkan inkonsistensi
Saya telah melihat situasi di mana pengembang memperbarui satu bagian dari basis kode sehingga berfungsi sepenuhnya berbeda dari bagian lain dalam upaya menjadikannya “lebih baik”. Hal ini sering kali menimbulkan kebingungan dan frustrasi bagi pengembang lain yang harus beralih konteks di antara gaya yang berbeda.
Katakanlah kita mempunyai aplikasi React dan kita selalu menggunakan React Query untuk mendapatkan data:
// Throughout the app
import { useQuery } from 'react-query';
function UserProfile({ userId }) {
const { data: user, isLoading } = useQuery(['user', userId], fetchUser);
if (isLoading) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
Sekarang, katakanlah seorang pengembang memutuskan untuk menggunakan Redux Toolkit hanya untuk satu komponen:
// One-off component
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchPosts } from './postsSlice';
function PostList() {
const dispatch = useDispatch();
const { posts, status } = useSelector((state) => state.posts);
useEffect(() => {
dispatch(fetchPosts());
}, [dispatch]);
if (status === 'loading') return <div>Loading...</div>;
return <div>{posts.map(post => <div key={post.id}>{post.title}</div>)}</div>;
}
Ketidakkonsistenan ini membuat frustrasi karena memperkenalkan model pengelolaan negara yang sangat berbeda hanya untuk satu komponen.
Pendekatan yang lebih baik adalah tetap menggunakan React Query:
// Consistent approach
import { useQuery } from 'react-query';
function PostList() {
const { data: posts, isLoading } = useQuery('posts', fetchPosts);
if (isLoading) return <div>Loading...</div>;
return <div>{posts.map(post => <div key={post.id}>{post.title}</div>)}</div>;
}
Rilis ini menjaga konsistensi, menggunakan React Query untuk mengambil data di seluruh aplikasi. Lebih sederhana dan tidak mengharuskan pengembang lain mempelajari pola baru hanya untuk satu komponen.
Ingat, konsistensi basis kode itu penting. Jika Anda perlu memperkenalkan model baru, pertama-tama pikirkan tentang cara mendapatkan dukungan dari tim Anda daripada menciptakan ketidakkonsistenan yang hanya terjadi satu kali saja.
4. Tidak memahami kode sebelum melakukan refactoring
Salah satu masalah terbesar yang saya lihat adalah pemfaktoran ulang kode meskipun pelajarilah secara berurutan tiba Pelajari itu. Ini adalah ide yang buruk. Saya melihat seseorang berkomentar bahwa Anda harus menggunakan kode tertentu selama 6-9 bulan. Jika tidak, Anda mungkin membuat kesalahan, mengganggu kinerja, dll.
maju
// a bit too much hard coded stuff here
function fetchUserData(userId: string) {
const cachedData = localStorage.getItem(`user_${userId}`);
if (cachedData) {
return JSON.parse(cachedData);
}
return api.fetchUser(userId).then(userData => {
localStorage.setItem(`user_${userId}`, JSON.stringify(userData));
return userData;
});
}
pemfaktoran ulang yang buruk
// where did the caching go?
function fetchUserData(userId: string) {
return api.fetchUser(userId);
}
Para pemfaktor ulang mungkin mengira mereka menyederhanakan kode, namun sebenarnya mereka menghapus mekanisme caching penting yang ada untuk mengurangi panggilan API dan meningkatkan kinerja.
pemfaktoran ulang yang baik
// cleaner code preserving the existing behavior
async function fetchUserData(userId: string) {
const cachedData = await cacheManager.get(`user_${userId}`);
if (cachedData) {
return cachedData;
}
const userData = await api.fetchUser(userId);
await cacheManager.set(`user_${userId}`, userData, { expiresIn: '1h' });
return userData;
}
Pemfaktoran ulang ini mempertahankan perilaku cache sekaligus berpotensi memperbaikinya melalui penggunaan pengelola cache kedaluwarsa yang lebih canggih.
5. Memahami lingkungan bisnis
Saya pernah bergabung dengan perusahaan yang memiliki beban kode warisan yang buruk. Saya memimpin proyek untuk memigrasikan perusahaan e-niaga ke teknologi baru, modern, lebih cepat, dan lebih baik…Angular.js.
Ternyata bisnis ini sangat bergantung pada SEO dan kami membangun aplikasi satu halaman yang lambat dan membengkak.
Selama dua tahun, kami belum mempublikasikan apa pun kecuali situs yang lambat, bermasalah, dan sulit dipelihara. Mengapa? Orang yang memimpin proyek ini (saya: Saya bajingan dalam adegan ini) belum pernah bekerja di situs ini sebelumnya. Saya masih muda dan bodoh.
Mari kita lihat contoh modern dari kesalahan ini:
pemfaktoran ulang yang buruk
// a single page app for an SEO-focused site is a bad idea
function App() {
return (
<Router>
<Switch>
<Route path="/product/:id" component={ProductDetails} />
</Switch>
</Router>
);
}
Pendekatan ini mungkin terlihat modern dan bersih, tetapi sepenuhnya ditampilkan di sisi klien. Untuk situs e-commerce yang sangat bergantung pada SEO, hal ini bisa menjadi bencana.
pemfaktoran ulang yang baik
// server render an SEO-focused site
export const getStaticProps: GetStaticProps = async () => {
const products = await getProducts();
return { props: { products } };
};
export default function ProductList({ products }) {
return (
<div>
...
</div>
);
}
Pendekatan berbasis Next.js ini menyediakan rendering sisi server yang siap pakai, yang sangat penting untuk SEO. Ini juga memberikan pengalaman pengguna yang lebih baik dengan pemuatan halaman awal yang lebih cepat dan peningkatan kinerja bagi pengguna dengan koneksi yang lebih lambat. Remix juga cocok untuk tujuan ini, memberikan manfaat serupa untuk rendering sisi server dan optimasi SEO.
6. Integrasi kode yang berlebihan
Saya pernah mempekerjakan seorang pria yang segera mulai memfaktorkan ulang kode pada hari pertamanya bekerja di backend kami. Kami memiliki banyak fungsi Firebase, beberapa di antaranya memiliki pengaturan berbeda dari yang lain, seperti batas waktu dan alokasi memori.
Ini adalah pengaturan awal kami.
maju
// we had this same code 40+ times in the codebase, we could perhaps consolidate
export const quickFunction = functions
.runWith({ timeoutSeconds: 60, memory: '256MB' })
.https.onRequest(...);
export const longRunningFunction = functions
.runWith({ timeoutSeconds: 540, memory: '1GB' })
.https.onRequest(...);
Orang ini memutuskan untuk menggabungkan semua fitur ini menjadi satu createApi Fungsi.
pemfaktoran ulang yang buruk
// blindly consolidating settings that should not be
const createApi = (handler: RequestHandler) => {
return functions
.runWith({ timeoutSeconds: 300, memory: '512MB' })
.https.onRequest((req, res) => handler(req, res));
};
export const quickFunction = createApi(handleQuickRequest);
export const longRunningFunction = createApi(handleLongRunningRequest);
Pemfaktoran ulang ini menyetel semua API agar memiliki setelan yang sama dan tidak dapat menimpa setiap API. Ini menjadi masalah karena terkadang kita memerlukan pengaturan berbeda untuk fungsi berbeda.
Pendekatan yang lebih baik adalah dengan mengizinkan opsi Firebase melalui setiap API.
pemfaktoran ulang yang baik
// setting good defaults, but letting anyone override
const createApi = (handler: RequestHandler, options: ApiOptions = {}) => {
return functions
.runWith({ timeoutSeconds: 300, memory: '512MB', ...options })
.https.onRequest((req, res) => handler(req, res));
};
export const quickFunction = createApi(handleQuickRequest, { timeoutSeconds: 60, memory: '256MB' });
export const longRunningFunction = createApi(handleLongRunningRequest, { timeoutSeconds: 540, memory: '1GB' });
Dengan cara ini, kami mempertahankan manfaat abstraksi dan fleksibilitas yang kami perlukan. Saat menggabungkan atau mengabstraksi, selalu pertimbangkan kasus penggunaan yang Anda layani. Jangan korbankan fleksibilitas untuk kode yang “lebih bersih”. Pastikan abstraksi Anda memungkinkan fungsionalitas penuh yang disediakan oleh implementasi asli.
Serius, pahami kodenya sebelum Anda mulai “memperbaikinya”. Saat berikutnya kami menerapkan beberapa API, kami menemui beberapa masalah yang sebenarnya bisa dihindari tanpa pemfaktoran ulang buta ini.
Cara melakukan refactor dengan benar
Perlu diperhatikan bahwa Anda benar-benar perlu memfaktorkan ulang kode Anda. Tapi lakukan dengan benar. Kode kita tidak sempurna, kode kita perlu dibersihkan, tetapi konsistensi dengan basis kode, pemahaman terhadap kode, dan kecerobohan tentang abstraksi.
Berikut beberapa tip agar refactoring berhasil:
- Inkremental: Lakukan perubahan kecil yang dapat dikelola, bukan perubahan menyeluruh.
- Pahami kode secara mendalam sebelum melakukan pemfaktoran ulang besar-besaran atau abstraksi baru.
- Cocokkan gaya pengkodean yang ada: Konsistensi adalah kunci pemeliharaan.
- Hindari terlalu banyak abstraksi baru: buatlah tetap sederhana kecuali kompleksitasnya benar-benar diperlukan.
- Hindari menambahkan perpustakaan baru tanpa dukungan tim, terutama perpustakaan dengan gaya pemrograman yang sangat berbeda.
- Tulis tes sebelum melakukan refactoring dan terus perbarui. Ini memastikan Anda mempertahankan fungsionalitas asli.
- Mintalah kolega Anda bertanggung jawab terhadap prinsip-prinsip ini.

Alat dan teknik untuk pemfaktoran ulang yang lebih baik
Untuk membantu memastikan bahwa pemfaktoran ulang Anda bermanfaat dan bukannya merugikan, pertimbangkan teknik dan alat berikut:
Alat pengorganisasian
Gunakan alat linting untuk menerapkan gaya pengkodean yang konsisten dan menangkap potensi masalah. Prettier dapat membantu memformat secara otomatis ke gaya yang konsisten, sementara ESLint dapat membantu dengan pemeriksaan konsistensi yang lebih terperinci yang dapat Anda sesuaikan dengan mudah menggunakan plugin Anda sendiri.
tinjauan kode
Lakukan peninjauan kode secara menyeluruh untuk mendapatkan masukan dari rekan sebelum menggabungkan kode yang telah difaktorkan ulang. Hal ini membantu mengidentifikasi potensi masalah sejak dini dan memastikan kode yang difaktorkan ulang memenuhi standar dan harapan tim.
tes
Tulis dan jalankan pengujian untuk memastikan kode yang difaktorkan ulang tidak merusak fungsionalitas yang ada. Vitest adalah test runner yang sangat cepat, andal, dan mudah digunakan yang tidak memerlukan konfigurasi apa pun secara default. Untuk pengujian visual, pertimbangkan Buku Cerita. React Test Library adalah sekumpulan utilitas yang bagus untuk menguji komponen React (juga Angular dan banyak varian lainnya).
(kanan) alat AI
Biarkan AI membantu Anda dalam upaya pemfaktoran ulang, setidaknya yang sesuai dengan gaya dan konvensi pengkodean yang ada.
Visual Copilot adalah alat yang sangat berguna untuk menjaga konsistensi saat coding di front end. Alat bertenaga AI ini membantu Anda menerjemahkan desain ke dalam kode sambil mencocokkan gaya pengkodean yang ada dan memanfaatkan komponen dan token sistem desain Anda dengan tepat.
sebagai kesimpulan
Pemfaktoran ulang adalah bagian penting dalam pengembangan perangkat lunak, namun harus bijaksana dan menghormati basis kode dan dinamika tim yang ada. Tujuan dari pemfaktoran ulang adalah untuk memperbaiki struktur internal kode tanpa mengubah perilaku eksternalnya.
Ingat: pemfaktoran ulang terbaik sering kali tidak terlihat oleh pengguna akhir, namun membuat pekerjaan pengembang lebih mudah. Mereka meningkatkan keterbacaan, pemeliharaan, dan efisiensi tanpa merusak keseluruhan sistem.
Lain kali Anda tergoda untuk membuat “rencana besar” untuk sebuah kode, mundurlah selangkah. Pahami secara menyeluruh, pertimbangkan dampak perubahan, dan lakukan perbaikan bertahap dan tim Anda akan berterima kasih.
Di masa depan, Anda (dan kolega Anda) akan menghargai pendekatan bijaksana ini untuk menjaga basis kode Anda tetap bersih dan terpelihara.
video
Oh, apakah kamu juga suka video YouTube? Ada juga video tentang ini, dari Anda sebenarnya:
rencana pengembangan website
metode pengembangan website
jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website
#Refactoring #yang #baik #dan #refactoring #yang #buruk