Disiplin Programming — MJR

Jihad MJR
AdHub Team
Published in
6 min readMar 18, 2019

Halo sobat AdHub, hari ini saya ingin berbagi pengetahuan saya mengenai Clean Code. Saat ini tim kami sudah mencapai sprint ke-3 dan tidak lama lagi kami akan menghadapi UTS, sprint terasa cukup berat dengan banyaknya tugas-tugas kuliah. Akan terdapat 3 topik yang ingin saya ceritakan pada blog kali ini, yaitu clean code, persona, dan deployment.

A. Clean Code

“Truth can only be found in one place: the code.”

Robert C. Martin

Programmer yang profesional tidak hanya mementingkan bagaimana software yang dibuatnya bisa berjalan, tapi juga memiliki semangat software craftmanship. Salah satu aspek software craftmanship adalah bagaimana menulis source code dengan baik sehingga software dapat maintainable dan mudah dikembangkan untuk jangka panjang.

Berikut ini hal mengenai Clean Code yang dapat membantu kita menuliskan code yang baik.

Penamaan Yang Jelas

Penamaan variable, method, dan class haruslah jelas, deskriptif, dan konsisten dalam proses pengembangan. Konsistensi yang dimaksud adalah tim pengembang harus menentukan sejak awal pengembangan apakah ingin menggunakan snake atau camel case dalam penamaan variable, method, class dan lainnya.

Gunakan kata benda untuk class dan objek. Kata kerja hanya digunakan untuk method. Nama method didahului oleh kata kerja sebagai predikat dan diakhiri oleh nama variable yang diakses/dimutasi/dikembalikan.

class Pembayaran(models.Model):
id = models.OneToOneField(Iklan, primary_key=True, on_delete=models.CASCADE)
status = models.CharField(max_length=20)
total = models.IntegerField()
pemasang_iklan = models.ForeignKey(PemasangIklan, on_delete=models.CASCADE)

Dalam penggunaan nama Class kita menggunakan camel case dan untuk variable menggunakan snake case. Camel case merupakan bentuk penulisan dengan menghubungkan kata berikutnya dengan Uppercase sebagai contoh PemasangIklan(). Sedangkan untuk snake case adalah menghubungkan antar kata dengan “_” seperti contoh yang diatas adalah pemasang_iklan

Formatting

Ketika membuat code sebaiknya kita menerapkan formatting code (layout) yang sesuai dengan bahasa pemrograman yang digunakan. Misalnya untuk bahasa pemrograman python harus mengikuti PEP-8.

Mungkin kita sebagai programmer kurang kenal dengan sistem PEP-8 atau formatting. Namun terdapat IDE yang dapat memudahkan penulisan formatting yaitu Jetbrains IDE. Pycharm sebagai IDE pyhton. Terdapat command untuk melakukan auto formating yaitu

Windows: ctrl + alt + l
MacOs: Command + alt + l

Dengan adanya auto formating, program kita akan menjadi lebih enak untuk dilihat dan dibaca tanpa menghafal setiap aturan PEP-8. Berikut saya berikan visualisasinya

Sebelum
Seusah dilakukan auto formatting

Hanya dengan menggunakan command diatas, sehingga kesalahan whitespace atau indentasi yang berantakan dapat disolve dengan mudah. Jika menggunakan VSCode kalian dapat mendownload extention beutify code untuk merapihkan code anda berdasarkan aturan formating yang sudah ditetapkan.

Comments

Comment secara umum digunakan untuk menjelaskan bagian code yang kurang bagus. Namun setelah mengikuti guest lecture pada pekan ke-5 kuliah, bawah penggunaan comment kurang disarankan. Salah satu alasannya adalah karena ketika seorang pengembang menulis comment mengindikasikan code yang dituliskan tidak deskriptif.

Salah satu penggunaan comment yang baik adalah untuk menginformasikan jika terdapat external library yang digunakan seperti API. Comment dapat berupa penjelasan singkat fungsi yang bekerja dan apa yang dikembalikan.

Function

Sebuah function haruslah memiliki nama yang jelas, hanya memiliki satu fungsi atau tujuan (hanya melakukan satu hal). Apabila sebuah fungsi dirasa terlalu besar dan memiliki lebih dari satu fungsi, fungsi tersebut dapat direfactor menjadi beberapa fungsi lainnya yang memiliki tujuan yang berbeda dengan teknik refactoring Extract Function.

Contoh Implementasi:

@csrf_exempt
def set_cookie(request):
check = dict(request.POST.lists())
platform = check["platform[]"]
response = HttpResponse("cookie set")
response.set_cookie('platform', platform)
return response

Dapat dilihat pada fungsi diatas, bahwa fungsi set_cookie memiliki satu tujuan yaitu menyimpan input kedalam cookie.

Test Driven Development (TDD)

Untuk setiap error yang mungkin terjadi perlu diantisipasi dengan handling yang sesuai. Dalam melakukan error handling usahakan untuk menuliskan statement Try-Except-Finally (pada Python) terlebih dulu. Selain itu dalam error handling sebaiknya kita tidak mengembalikan (return) nilai null.

Berikut contoh test yang kami buat, kami melakukan prinsip TDD yaitu membuat test sebelum mengimplementasikan program.

def test_index_view_with_cookies(self):
url = "/platform-iklan/cookie/"
data = {
'platform[]': ['google', 'facebook']
}
resp = self.client.post(url, data, follow=True)
self.assertEqual(resp.status_code, 200)
url2 = "/platform-iklan"
resp = self.client.get(url2, follow=True)
self.assertEqual(resp.status_code, 200)

cookie = {}
datacookie = self.client.cookies.items()

for k, v in datacookie:
cookie[k] = v
self.assertIn('platform', cookie)

IMPLEMENTASI DALAM PPL

Models.py yang kami buat sudah mengikuti kaida Clean code dengan penamaan class dan variable yang sudah deskriptif. Dengan menggunakan format PEP-8. Sebelum membuat models kami sudah membuat test dalam tahap pengembangan.

Models.py

class PemasangIklan(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
full_name = models.CharField(max_length=30)
telepon = models.CharField(max_length=12)
customer_id = models.CharField(max_length=12, default='0')


class Iklan(models.Model):
PLATFORM_IKLAN = (
(1, 'Google'),
(2, 'Facebook')
)
id = models.AutoField(primary_key=True)
judul = models.CharField(max_length=100)
platform = models.IntegerField(choices=PLATFORM_IKLAN)
date_created = models.DateTimeField()
date_modified = models.DateTimeField()
deskripsi = models.TextField()
image = models.ImageField()
link_iklan = models.URLField()
pemasang_iklan = models.ForeignKey(PemasangIklan, on_delete=models.CASCADE)

test.py

class IklanModelsTest(TestCase):
def setUp(self):
self.user1 = User.objects.create(username='user1', email='user1@mail.com')
self.user2 = User.objects.create(username='user2', email='user2@mail.com')
self.pengiklan = PemasangIklan.objects.create(user=self.user1, full_name='m fakhruddin hafizh',
telepon='081285496882')
self.image = tempfile.NamedTemporaryFile(suffix='.jpg').name
self.iklan = Iklan.objects.create(judul='Jual Makanan RIngan Enak!', platform=1,
date_created=datetime.datetime.now(),
date_modified=datetime.datetime.now(),
deskripsi='Makanan ringan yang tidak mengandung micin tapi enak',
image=self.image, link_iklan='https://www.google.com/',
pemasang_iklan=self.pengiklan)

def test_models_create_pemasangIKlan(self):
pengiklan = PemasangIklan.objects.create(user=self.user2, full_name='m f hafiz', telepon='081285495482')
jumlah_pengiklan = PemasangIklan.objects.all().count()
self.assertEqual(jumlah_pengiklan, 2)

Contoh implementasi Try Catch untuk error 500

Contoh halaman ketika terjadi page error dalam aplikasi Ad-Hub

B. Persona

Persona adalah sebuah representasi pengguna dalam bentuk individu imajiner yang memuat rangkuman singkat mengenai karakteristik, pengalaman, tujuan, tasks, pain points, dan kondisi lingkungan pengguna yang sebenarnya. Persona juga bisa digunakan untuk membangun fokus, berkomunikasi dan mencapai konsensus, membuat keputusan, dan mengukur efektifitas. Sebuah persona harus menggambarkan keadaan pengguna yang sebenarnya.

Contoh persona pada proyek yang kami kembangkan yaitu Pemasang Iklan (Ditingkat UKM). Pemasang iklan ingin dimudahkan dengan adanya aplikasi pemasangan iklan yang multi platform. Pemasang iklan juga dapat melihat laporan terhadap iklan yang telah mereka pasang.

C. Deployment

Terdapat 3 stages pada CI/CD aplikasi AdHub, yaitu linting, testing dan building. Linting merupakan tahap memeriksa format atau code style dari project. Dalam project ini, kami menggunakan python dengan format PEP-8 dan library pylint dan pylint django.

.gitlab-ci.ymllint:
stage: lint
script:
- pylint --load-plugins pylint_django *.py || true

Setelah melakukan proses linting, tahap berikutnya adalah testing. Pada tahap ini kami menggunakan modul pada pip, yaitu coverage. Pertama development server dijalankan di background untuk mengetes apakah aplikasi dapat dijalankan atau tidak. Kemudian semua test pada semua module app akan dijalankan, sambil mengukur code coverage yang dicakup kecuali file manage.py. Karena kami menggunakan TDD maka akan terdapat fase RED dan GREEN. Red merupakan kondisi ketika test gagal yaitu pada fase penulisan code. Kemudian pengembang akan mengimplementasi program dari test yang dibuat kemudian state akan menjadi GREEN.

test:
stage: test
script:
- python manage.py runserver 8000 &
- coverage run --omit='manage.py' manage.py test
- coverage report -m

Proses terakhir adalah building, proses ini membuat docker image yang kemudian dipush ke registry PPL.

build_development:image: docker:stablestage: buildonly:
- development
services:
- docker:dind
tags:
- build
- docker
before_script:
- docker info
script:
- docker build -t registry.docker.ppl.cs.ui.ac.id/ppla2/dev:latest .
- docker push registry.docker.ppl.cs.ui.ac.id/ppla2/dev:latest

Setelah melakukan push, kita dapat melakukan pull image agar image yang sudah dipush dapat digunakan. Setelah melakukan pull image, dapat dilihat pada bagian ‘stacks’ bahwa stacks sudah tersedia. Setelah itu dapat dilakukan update stacks untuk mengupdate container.

Berikut potongan code .gitlab-ci.yml

image: python:latest
stages:
- lint
- test
- build
variables:
HTTP_PROXY: http://proxy.cs.ui.ac.id:8080
HTTPS_PROXY: http://proxy.cs.ui.ac.id:8080
FTP_PROXY: http://proxy.cs.ui.ac.id:8080
NO_PROXY: "localhost,127.0.0.1,gitlab.cs.ui.ac.id,docker:2375,docker:2376"
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
ENVIRONMENT: GITLAB
before_script:- pip install -r requirements.txt- python manage.py makemigrations- python manage.py migrate- python manage.py collectstatic --no-inputlint:
stage: lint
script:
- pylint --load-plugins pylint_django *.py || true
test:
stage: test
script:
- python manage.py runserver 8000 &- coverage run --omit='manage.py' manage.py test- coverage report -m
build_development:
image: docker:stablestage: buildonly:- developmentservices:- docker:dindtags:- build- dockerbefore_script:- docker infoscript:- docker build -t registry.docker.ppl.cs.ui.ac.id/ppla2/dev:latest .- docker push registry.docker.ppl.cs.ui.ac.id/ppla2/dev:latestbuild_staging:image: docker:stablestage: buildonly:- stagingservices:- docker:dindtags:- build- dockerbefore_script:- docker infoscript:- docker build -t registry.docker.ppl.cs.ui.ac.id/ppla2/staging:latest .- docker push registry.docker.ppl.cs.ui.ac.id/ppla2/staging:latestbuild_production:image: docker:stablestage: buildonly:- masterservices:- docker:dindtags:- build- dockerbefore_script:- docker infoscript:- docker build -t registry.docker.ppl.cs.ui.ac.id/ppla2/production:latest .- docker push registry.docker.ppl.cs.ui.ac.id/ppla2/production:latest

--

--