Sebuah kisah tentang bagaimana tim freelancer menulis aplikasi JavaScript penuh-tumpukan

Penulis materi, terjemahan yang kami terbitkan hari ini, mengatakan bahwa repositori GitHub , tempat ia bekerja dan beberapa freelancer lainnya, menerima, karena berbagai alasan, sekitar 8.200 bintang dalam 3 hari. Repositori ini menempati urutan pertama pada HackerNews dan GitHub Trending, dan 20.000 pengguna Reddit memilihnya.



Repositori ini mencerminkan metodologi untuk mengembangkan aplikasi tumpukan penuh, yang ditujukan untuk artikel ini.

Latar belakang


Saya akan menulis materi ini untuk beberapa waktu. Saya percaya bahwa saya tidak dapat menemukan momen yang lebih baik daripada ini ketika repositori kami sangat populer.


No. 1 di Tren GitHub

Saya bekerja di tim freelancer . Proyek kami menggunakan React / React Native, NodeJS, dan GraphQL. Materi ini ditujukan bagi mereka yang ingin belajar tentang bagaimana kami mengembangkan aplikasi. Selain itu, ini akan bermanfaat bagi mereka yang akan bergabung dengan tim kami di masa depan.

Sekarang saya akan berbicara tentang prinsip-prinsip dasar yang kami gunakan ketika mengembangkan proyek.

Lebih sederhana, semakin baik.


"Semakin sederhana semakin baik" lebih mudah diucapkan daripada dilakukan. Sebagian besar pengembang menyadari bahwa kesederhanaan adalah prinsip penting dalam pengembangan perangkat lunak. Tetapi prinsip ini tidak selalu mudah diikuti. Jika kodenya sederhana, ini memfasilitasi dukungan proyek dan menyederhanakan kerja tim pada proyek ini. Selain itu, kepatuhan terhadap prinsip ini membantu dalam bekerja dengan kode yang ditulis, katakanlah, enam bulan lalu.

Berikut adalah kesalahan yang saya temui mengenai prinsip ini:

  • Keinginan yang tidak dapat dibenarkan untuk memenuhi prinsip KERING. Terkadang menyalin dan menempel kode cukup normal. Tidak perlu abstrak setiap 2 fragmen kode yang agak mirip satu sama lain. Saya sendiri melakukan kesalahan ini. Semua, mungkin, melakukan itu. KERING adalah pendekatan pemrograman yang baik, tetapi memilih abstraksi yang gagal hanya dapat memperburuk situasi dan memperumit basis kode. Jika Anda ingin tahu lebih banyak tentang ide-ide ini, saya sarankan membaca Program AHA Kent A Dodds '.
  • Penolakan untuk menggunakan alat yang tersedia. Salah satu contoh kesalahan ini adalah penggunaan reduce alih-alih map atau filter . Tentu saja, menggunakan reduce dapat mereproduksi perilaku map . Tetapi ini cenderung mengarah pada peningkatan ukuran kode, dan fakta bahwa akan lebih sulit bagi orang lain untuk memahami kode ini, mengingat bahwa "kesederhanaan kode" adalah konsep subyektif. Terkadang mungkin perlu menggunakan hanya reduce . Dan jika Anda membandingkan kecepatan pemrosesan set data menggunakan map dan filter panggilan yang digabungkan dalam rantai, dan menggunakan reduce , ternyata opsi kedua bekerja lebih cepat. Dalam opsi dengan reduce Anda harus melihat kumpulan nilai satu kali, bukan dua. Di depan kita ada perdebatan tentang produktivitas dan kesederhanaan. Dalam kebanyakan kasus, saya lebih suka kesederhanaan dan mencoba untuk menghindari optimasi kode prematur, yaitu, saya akan memilih pasangan map / filter daripada reduce . Dan jika ternyata pembangunan map dan filter menjadi hambatan sistem, itu akan menerjemahkan kode untuk reduce .

Banyak ide yang akan dibahas di bawah ini bertujuan untuk membuat basis kode sesederhana mungkin dan mempertahankannya dalam keadaan ini.

Dekatkan entitas serupa satu sama lain


Prinsip ini, “prinsip colocation,” berlaku untuk banyak bagian aplikasi. Ini adalah struktur folder tempat kode klien dan server disimpan, ini adalah penyimpanan kode proyek dalam satu repositori, ini juga merupakan pengambilan keputusan tentang kode mana yang ada dalam file tertentu.

▍ Gudang


Disarankan agar Anda menyimpan kode klien dan server dalam repositori yang sama. Sederhana saja. Jangan mempersulit apa yang tidak perlu rumit. Dengan pendekatan ini, akan lebih mudah untuk mengatur kerja tim yang terkoordinasi pada suatu proyek. Saya mengerjakan proyek yang menggunakan berbagai repositori untuk menyimpan bahan. Ini bukan bencana, tetapi repositori tunggal membuat hidup lebih mudah.

▍ Struktur proyek bagian klien dari aplikasi


Kami sedang menulis aplikasi full-stack. Yaitu, kode klien dan kode server. Struktur folder dari proyek klien tipikal menyediakan direktori terpisah untuk komponen, wadah, tindakan, reduksi dan rute.

Tindakan dan reduksi hadir dalam proyek-proyek yang menggunakan Redux. Saya berusaha keras untuk melakukannya tanpa perpustakaan ini. Saya yakin ada proyek-proyek berkualitas tinggi yang menggunakan struktur yang sama. Beberapa proyek saya memiliki folder terpisah untuk komponen dan wadah. Folder komponen dapat menyimpan sesuatu seperti file dengan kode untuk entitas seperti BlogPost dan Profile . Di folder penampung ada file yang menyimpan kode penampung BlogPostContainer dan ProfileContainer . Kontainer menerima data dari server dan meneruskannya ke komponen anak "bodoh", yang tugasnya menampilkan data ini di layar.

Ini adalah struktur kerja. Paling tidak homogen, dan ini sangat penting. Ini mengarah pada fakta bahwa pengembang, yang bergabung dengan proyek ini, dengan cepat memahami apa yang terjadi di dalamnya, dan peran apa yang dimainkan oleh masing-masing bagian. Kerugian dari pendekatan ini, karena saya baru-baru ini berusaha untuk tidak menggunakannya, adalah bahwa hal itu memaksa programmer untuk terus bergerak di sekitar basis kode. Sebagai contoh, BlogPostContainer dan BlogPostContainer tidak memiliki kesamaan, tetapi file mereka bersebelahan dan jauh dari file di mana mereka benar-benar digunakan.

Untuk beberapa waktu sekarang, saya telah berusaha untuk menempatkan file yang isinya direncanakan untuk dibagikan dalam folder yang sama. Pendekatan penataan proyek ini didasarkan pada pengelompokan file berdasarkan kemampuannya. Berkat pendekatan ini, Anda dapat sangat menyederhanakan hidup Anda jika, misalnya, Anda menempatkan komponen induk dan komponen anaknya yang "bodoh" di folder yang sama.

Biasanya kami menggunakan folder routes / screens dan folder components . Folder komponen biasanya menyimpan kode untuk item seperti Button atau Input . Kode ini dapat digunakan pada halaman aplikasi mana saja. Setiap folder dalam folder untuk rute adalah halaman aplikasi terpisah. Pada saat yang sama, file dengan kode komponen dan kode logika aplikasi yang terkait dengan rute ini berada di folder yang sama. Dan kode komponen yang digunakan pada beberapa halaman termasuk dalam folder components .

Dalam folder rute, Anda dapat membuat folder tambahan tempat kode yang bertanggung jawab untuk pembentukan berbagai bagian halaman dikelompokkan. Ini masuk akal dalam kasus di mana rute diwakili oleh sejumlah besar kode. Namun, di sini, saya ingin memperingatkan pembaca bahwa tidak layak membuat struktur dari folder dengan tingkat sarang yang sangat tinggi. Ini menyulitkan pergerakan proyek. Struktur folder bersarang yang dalam adalah salah satu tanda proyek yang terlalu rumit. Perlu dicatat bahwa penggunaan alat khusus, seperti perintah pencarian, memberikan programmer alat yang nyaman untuk bekerja dengan kode proyek dan untuk menemukan apa yang dia butuhkan. Tetapi struktur file proyek juga mempengaruhi kegunaannya.

Dengan menyusun kode proyek, Anda dapat mengelompokkan file berdasarkan bukan pada rute, tetapi pada kemampuan proyek yang diterapkan oleh file-file ini. Dalam kasus saya, pendekatan ini menunjukkan dirinya dengan sempurna pada proyek satu halaman yang mengimplementasikan banyak fitur pada satu-satunya halaman mereka. Namun perlu dicatat bahwa pengelompokan material proyek berdasarkan rute lebih mudah. Pendekatan ini tidak memerlukan upaya mental khusus untuk membuat keputusan tentang entitas mana yang harus ditempatkan bersebelahan, dan untuk mencari sesuatu.

Jika kita melanjutkan pengelompokan kode lebih lanjut, kita dapat memutuskan bahwa kode kontainer dan komponen akan ditempatkan di file yang sama. Dan Anda dapat melangkah lebih jauh - memasukkan kode dua komponen dalam satu file. Saya kira Anda mungkin berpikir sekarang bahwa merekomendasikan hal-hal seperti itu benar-benar penistaan. Namun dalam kenyataannya, semuanya jauh dari buruk. Bahkan, pendekatan ini sepenuhnya dibenarkan. Dan jika Anda menggunakan React hooks, atau kode yang dihasilkan (atau keduanya), saya akan merekomendasikan pendekatan ini.

Faktanya, pertanyaan tentang cara menguraikan kode ke dalam file tidak terlalu penting. Pertanyaan sebenarnya adalah mengapa Anda mungkin perlu membagi komponen menjadi pintar dan bodoh. Apa manfaat dari pemisahan ini? Ada beberapa jawaban untuk pertanyaan ini:

  1. Aplikasi yang dibangun dengan cara ini lebih mudah untuk diuji.
  2. Mengembangkan aplikasi seperti itu membuatnya lebih mudah untuk menggunakan alat-alat seperti Storybook.
  3. Komponen bodoh dapat digunakan dengan banyak komponen pintar yang berbeda (dan sebaliknya).
  4. Komponen pintar dapat digunakan pada platform yang berbeda (misalnya, pada platform Bereaksi dan Bereaksi Asli).

Semua ini adalah argumen nyata yang mendukung pembagian komponen menjadi "pintar" dan "bodoh", tetapi mereka tidak berlaku untuk semua situasi. Misalnya, kami sering menggunakan Klien Apollo dengan kait saat membuat proyek. Untuk menguji proyek semacam itu, Anda dapat membuat tiruan Apollo atau kait. Hal yang sama berlaku untuk Buku Cerita. Jika kita berbicara tentang mencampur dan berbagi komponen "pintar" dan "bodoh", maka, pada kenyataannya, saya belum pernah bertemu ini dalam praktik. Mengenai penggunaan lintas-platform dari kode, ada satu proyek di mana saya akan melakukan sesuatu yang serupa, tetapi tidak pernah melakukannya. Itu seharusnya menjadi repositori Lerna mono . Saat ini, alih-alih pendekatan ini, Anda dapat memilih untuk React Native Web.

Akibatnya, kita dapat mengatakan bahwa dalam pemisahan komponen menjadi "pintar" dan "konyol" ada makna tertentu. Ini adalah konsep penting yang perlu diketahui. Namun seringkali, Anda tidak perlu terlalu khawatir tentang hal itu, terutama mengingat penampilan React hooks baru-baru ini.

Titik kuat menggabungkan kemampuan komponen "pintar" dan "bodoh" dalam satu entitas adalah mempercepat pengembangan, dan menyederhanakan struktur kode.

Selain itu, jika kebutuhan seperti itu muncul, komponen selalu dapat dibagi menjadi dua komponen yang terpisah - "pintar" dan "bodoh".

Stilisasi


Kami menggunakan komponen emosi / gaya untuk aplikasi gaya. Selalu ada godaan untuk memisahkan gaya menjadi file terpisah. Saya telah melihat beberapa pengembang melakukan ini. Tetapi, setelah saya mencoba kedua pendekatan tersebut, pada akhirnya saya tidak dapat menemukan alasan untuk memindahkan style ke file yang terpisah. Seperti dalam banyak hal lain, yang kita bicarakan di sini, seorang pengembang dapat membuat hidupnya lebih mudah dengan menggabungkan gaya dan komponen yang mereka hubungkan dalam satu file.

▍ Struktur proyek dari bagian server dari aplikasi


Semua hal di atas benar sehubungan dengan penataan kode sisi server dari aplikasi. Struktur khas yang saya coba hindari secara pribadi mungkin terlihat seperti ini :

 src │ app.js #     └───api #   Express      └───config #      └───jobs #    agenda.js └───loaders #     └───models #    └───services # - └───subscribers #      └───types #    (d.ts)  Typescript 

Kami biasanya menggunakan GraphQL di proyek kami. Oleh karena itu, mereka menggunakan file yang menyimpan model, layanan, dan pengenal. Alih-alih menyebarkannya di berbagai tempat proyek, saya mengumpulkannya dalam satu folder. Paling sering, file-file ini akan dibagikan, dan akan lebih mudah untuk bekerja dengannya jika disimpan di folder yang sama.

Jangan menimpa definisi tipe berkali-kali


Kami menggunakan banyak solusi dalam proyek kami yang entah bagaimana terkait dengan tipe data. Ini adalah TypeScript, GraphQL, skema basis data, dan kadang-kadang MobX. Sebagai hasilnya, mungkin ternyata tipe untuk entitas yang sama dijelaskan 3-4 kali. Hal-hal seperti ini harus dihindari. Kita harus berusaha menggunakan alat yang secara otomatis menghasilkan deskripsi tipe.

Di server, kombinasi TypeORM / Typegoose dan TypeGraphQL dapat digunakan untuk tujuan ini. Ini cukup untuk menggambarkan semua tipe yang digunakan. TypeORM / Typegoose memungkinkan Anda untuk menggambarkan skema basis data dan tipe-tipe TypeScript yang sesuai. TypeGraphQL akan membantu dalam menciptakan jenis-jenis GraphQL dan TypeScript.

Berikut adalah contoh menentukan jenis TypeORM (MongoDB) dan TypeGraphQL dalam satu file:

 import { Field, ObjectType, ID } from 'type-graphql' import { Entity, ObjectIdColumn, ObjectID, Column, CreateDateColumn, UpdateDateColumn, } from 'typeorm' @ObjectType() @Entity() export default class Policy { @Field(type => ID) @ObjectIdColumn() _id: ObjectID @Field() @CreateDateColumn({ type: 'timestamp' }) createdAt: Date @Field({ nullable: true }) @UpdateDateColumn({ type: 'timestamp', nullable: true }) updatedAt?: Date @Field() @Column() name: string @Field() @Column() version: number } 

GraphQL Code Generator juga dapat menghasilkan berbagai jenis. Kami menggunakan alat ini untuk membuat jenis TypeScript pada klien, serta Bereaksi kait yang mengakses server.

Jika Anda menggunakan MobX untuk mengontrol keadaan aplikasi, kemudian menggunakan beberapa baris kode, Anda bisa mendapatkan tipe TS yang dihasilkan secara otomatis. Jika Anda juga menggunakan GraphQL, maka Anda harus melihat pada paket baru - MST-GQL , yang menghasilkan pohon negara dari skema GQL.

Menggunakan alat-alat ini bersama-sama akan menyelamatkan Anda dari penulisan ulang sejumlah besar kode dan membantu Anda menghindari kesalahan umum.

Solusi lain, seperti Prisma , Hasura, dan AWS AppSync , juga dapat membantu menghindari deklarasi tipe duplikat. Penggunaan alat-alat semacam itu, tentu saja memiliki pro dan kontra. Dalam proyek-proyek yang kami buat, alat-alat seperti itu tidak selalu digunakan, karena kami perlu menggunakan kode di server organisasi kami sendiri.

Kapan pun memungkinkan, gunakan cara pembuatan kode otomatis


Jika Anda melihat kode yang Anda buat tanpa menggunakan alat di atas untuk secara otomatis menghasilkan kode, ternyata pemrogram harus terus-menerus menulis hal yang sama. Saran utama yang dapat saya berikan tentang ini adalah Anda perlu membuat cuplikan untuk semua yang sering Anda gunakan. Jika Anda sering memasukkan perintah console.log , buat snippet like cl , yang secara otomatis berubah menjadi console.log() . Jika Anda tidak melakukan ini dan meminta saya untuk membantu Anda dengan debugging kode, itu akan membuat saya sangat sedih.

Ada banyak paket dengan snippet, tetapi mudah untuk membuat snippet Anda sendiri. Misalnya, menggunakan generator Snippet .

Berikut adalah kode yang memungkinkan saya untuk menambahkan beberapa cuplikan favorit saya ke Kode VS:

 { "Export default": {   "scope": "javascript,typescript,javascriptreact,typescriptreact",   "prefix": "eid",   "body": [     "export { default } from './${TM_DIRECTORY/.*[\\/](.*)$$/$1/}'",     "$2"   ],   "description": "Import and export default in a single line" }, "Filename": {   "prefix": "fn",   "body": ["${TM_FILENAME_BASE}"],   "description": "Print filename" }, "Import emotion styled": {   "prefix": "imes",   "body": ["import styled from '@emotion/styled'"],   "description": "Import Emotion js as styled" }, "Import emotion css only": {   "prefix": "imec",   "body": ["import { css } from '@emotion/styled'"],   "description": "Import Emotion css only" }, "Import emotion styled and css only": {   "prefix": "imesc",   "body": ["import styled, { css } from ''@emotion/styled'"],   "description": "Import Emotion js and css" }, "Styled component": {   "prefix": "sc",   "body": ["const ${1} = styled.${2}`", "  ${3}", "`"],   "description": "Import Emotion js and css" }, "TypeScript React Function Component": {   "prefix": "rfc",   "body": [     "import React from 'react'",     "",     "interface ${1:ComponentName}Props {",     "}",     "",     "const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = props => {",     " return (",     " <div>",     " ${1:ComponentName}",     " </div>",     " )",     "}",     "",     "export default ${1:ComponentName}",     ""   ],   "description": "TypeScript React Function Component" } } 

Selain potongan, generator kode dapat membantu menghemat waktu. Anda dapat membuatnya sendiri. Saya suka menggunakan celepuk untuk ini.

Angular memiliki generator kode bawaannya sendiri. Menggunakan alat-alat baris perintah, Anda dapat membuat komponen baru, yang terdiri dari 4 file, yang menyajikan segala sesuatu yang dapat Anda harapkan untuk ditemukan di komponen. Sangat disayangkan React tidak memiliki fitur standar seperti itu, tetapi sesuatu yang serupa dapat dibuat secara independen menggunakan plop. Jika setiap komponen baru yang Anda buat harus disajikan dalam bentuk folder yang berisi file dengan kode komponen, file tes dan file Storybook, generator akan membantu untuk membuat semua ini dengan satu perintah. Ini dalam banyak kasus sangat memudahkan kehidupan pengembang. Misalnya, saat menambahkan fitur baru ke server, jalankan saja satu perintah di baris perintah. Setelah itu, file entitas, layanan, dan pengenal akan secara otomatis dibuat berisi semua struktur dasar yang diperlukan.

Kekuatan lain dari pembuat kode adalah mereka berkontribusi pada keseragaman dalam pengembangan tim. Jika semua orang menggunakan generator plop yang sama, maka kodenya akan sangat seragam untuk semua orang.

Pemformatan kode otomatis


Memformat kode adalah tugas yang sederhana, tetapi, sayangnya, itu tidak selalu diselesaikan dengan benar. Jangan buang waktu untuk menyelaraskan kode secara manual atau memasukkan titik koma ke dalamnya. Gunakan Prettier untuk memformat kode secara otomatis saat melakukan.

Ringkasan


Dalam artikel ini saya memberi tahu Anda tentang beberapa hal yang kami pelajari selama bertahun-tahun bekerja, selama bertahun-tahun coba-coba. Ada banyak pendekatan untuk menyusun basis kode proyek. Namun di antara mereka tidak ada yang bisa disebut satu-satunya yang benar.

Hal paling penting yang ingin saya sampaikan kepada Anda adalah bahwa programmer harus berusaha untuk kesederhanaan dalam mengatur proyek, untuk homogenitas mereka, untuk menggunakan struktur yang mudah dimengerti yang mudah digunakan. Ini menyederhanakan proyek pengembangan tim.

Pembaca yang budiman! Apa pendapat Anda tentang ide-ide untuk mengembangkan aplikasi JavaScript tumpukan penuh yang diuraikan dalam artikel ini?



Source: https://habr.com/ru/post/id456340/


All Articles