Cum să optimizați aplicația web PHP Laravel pentru înaltă performanță?

Laravel este multe lucruri. Dar rapid nu este unul dintre ele. Să învățăm câteva trucuri ale meseriei pentru a o face să meargă mai repede!

Niciun dezvoltator PHP nu este atins de Laravel aceste zile. Ei sunt fie un dezvoltator junior sau de nivel mediu, care iubește dezvoltarea rapidă pe care o oferă Laravel, fie sunt un dezvoltator senior care este forțat să învețe Laravel din cauza presiunilor pieței.

Oricum ar fi, nu se poate nega că Laravel a revitalizat ecosistemul PHP (eu, cu siguranță, aș fi părăsit lumea PHP cu mult timp în urmă dacă Laravel nu ar fi fost acolo).

Un fragment de laudă de sine (oarecum justificată) de la Laravel

Cu toate acestea, deoarece Laravel se aplecă pe spate pentru a vă ușura lucrurile, înseamnă că dedesubt face tone și tone de muncă pentru a vă asigura că aveți o viață confortabilă ca dezvoltator. Toate caracteristicile „magice” ale Laravel care par să funcționeze au straturi peste straturi de cod care trebuie dezvoltate de fiecare dată când rulează o caracteristică. Chiar și o simplă excepție urmărește cât de adâncă este gaura iepurelui (observați unde începe eroarea, până la nucleul principal):

Pentru ceea ce pare a fi o eroare de compilare într-una dintre vizualizări, există 18 apeluri de funcție de urmărit. Am întâlnit personal 40 și ar putea fi cu ușurință mai multe dacă utilizați alte biblioteci și pluginuri.

Ideea fiind, în mod implicit, aceste straturi peste straturi de cod, fac Laravel să încetinească.

Cât de lent este Laravel?

Sincer, este simplu imposibil să răspunzi la această întrebare din mai multe motive.

În primul rând, nu există un standard acceptat, obiectiv și sensibil pentru măsurarea vitezei aplicațiilor web. Mai rapid sau mai lent în comparație cu ce? In ce conditii?

În al doilea rând, o aplicație web depinde de atât de multe lucruri (bază de date, sistem de fișiere, rețea, cache etc.) încât este o prostie să vorbim despre viteză. O aplicație web foarte rapidă cu o bază de date foarte lentă este o aplicație web foarte lentă. 🙂

Dar această incertitudine este tocmai motivul pentru care benchmark-urile sunt populare. Chiar dacă nu înseamnă nimic (vezi acest și acest), ele oferă un cadru de referință și ne ajută să ne înnebunim. Prin urmare, cu câteva vârfuri de sare gata, să ne facem o idee greșită, aproximativă, despre viteza dintre cadrele PHP.

Mergând pe acest GitHub destul de respectabil sursăiată cum se aliniază cadrele PHP în comparație:

S-ar putea să nu-l observați pe Laravel aici (chiar dacă vă mijiți foarte tare) decât dacă vă aruncați carcasa până la capătul cozii. Da, dragi prieteni, Laravel este ultimul! Acum, desigur, majoritatea acestor „cadre” nu sunt foarte practice sau chiar utile, dar ne spune cât de lent este Laravel în comparație cu altele mai populare.

În mod normal, această „încetinere” nu apare în aplicații, deoarece aplicațiile noastre web de zi cu zi ating rareori un număr mare. Dar odată ce o fac (să zicem, peste 200-500 de concurență), serverele încep să se sufoce și să moară. Este momentul în care nici măcar aruncarea mai multor hardware la problemă nu o reduce, iar facturile de infrastructură cresc atât de repede încât idealurile tale înalte de cloud computing se prăbușesc.

Dar hei, înveselește-te! Acest articol nu este despre ceea ce nu se poate face, ci despre ceea ce se poate face. 🙂

Vestea bună este că poți face multe pentru ca aplicația ta Laravel să meargă mai repede. De mai multe ori repede. Da, nu glumesc. Puteți face ca aceeași bază de cod să devină balistică și să economisiți câteva sute de dolari pe facturile de infrastructură/gazduire în fiecare lună. Cum? Să ajungem la asta.

Patru tipuri de optimizări

După părerea mea, optimizarea se poate face pe patru niveluri distincte (când vine vorba de aplicații PHP, adică):

  • La nivel de limbă: aceasta înseamnă că utilizați o versiune mai rapidă a limbii și evitați anumite caracteristici/stiluri de codare în limbajul care face codul dvs. lent.
  • La nivel de cadru: acestea sunt lucrurile pe care le vom acoperi în acest articol.
  • La nivel de infrastructură: ajustați managerul de procese PHP, serverul web, baza de date etc.
  • La nivel hardware: trecerea la un furnizor de găzduire hardware mai bun, mai rapid și mai puternic.

Toate aceste tipuri de optimizări au locul lor (de exemplu, optimizarea PHP-fpm este destul de critică și puternică). Dar accentul acestui articol va fi optimizările pur de tip 2: cele legate de cadru.

Apropo, nu există niciun motiv în spatele numerotării și nu este un standard acceptat. Tocmai le-am inventat. Vă rugăm să nu mă citați niciodată și să spuneți „Avem nevoie de optimizare de tip 3 pe serverul nostru”, altfel liderul echipei vă va ucide, mă va găsi și apoi mă va ucide și pe mine. 😀

Și acum, în sfârșit, ajungem la pământul promis.

Fiți conștienți de n+1 interogări la baza de date

Problema de interogare n+1 este una comună atunci când sunt utilizate ORM-uri. Laravel are ORM-ul său puternic numit Eloquent, care este atât de frumos, atât de convenabil, încât adesea uităm să ne uităm la ce se întâmplă.

  8 proiecte grozave pe care Apple le-a ucis ca și mașina sa electrică

Luați în considerare un scenariu foarte comun: afișarea listei tuturor comenzilor plasate de o anumită listă de clienți. Acest lucru este destul de comun în sistemele de comerț electronic și în orice interfețe de raportare în general, unde trebuie să afișăm toate entitățile legate de unele entități.

În Laravel, ne-am putea imagina o funcție de controler care face treaba astfel:

class OrdersController extends Controller 
{
    // ... 

    public function getAllByCustomers(Request $request, array $ids) {
        $customers = Customer::findMany($ids);        
        $orders = collect(); // new collection
        
        foreach ($customers as $customer) {
            $orders = $orders->merge($customer->orders);
        }
        
        return view('admin.reports.orders', ['orders' => $orders]);
    }
}

Dulce! Și mai important, elegant, frumos. 🤩🤩

Din păcate, este o modalitate dezastruoasă de a scrie cod în Laravel.

Iata de ce.

Când cerem ORM-ului să caute clienții dați, este generată o interogare SQL ca aceasta:

SELECT * FROM customers WHERE id IN (22, 45, 34, . . .);

Ceea ce este exact cum era de așteptat. Ca rezultat, toate rândurile returnate sunt stocate în colecția $clienți în cadrul funcției de controler.

Acum trecem peste fiecare client unul câte unul și primim comenzile. Aceasta execută următoarea interogare. . .

SELECT * FROM orders WHERE customer_id = 22;

. . . de câte ori există clienți.

Cu alte cuvinte, dacă trebuie să obținem datele comenzii pentru 1000 de clienți, numărul total de interogări în baza de date executate va fi 1 (pentru preluarea tuturor datelor clienților) + 1000 (pentru preluarea datelor despre comandă pentru fiecare client) = 1001. Aceasta de unde provine numele n+1.

Putem face mai bine? Cu siguranță! Folosind ceea ce este cunoscut sub numele de încărcare nerăbdătoare, putem forța ORM să efectueze un JOIN și să returneze toate datele necesare într-o singură interogare! Ca aceasta:

$orders = Customer::findMany($ids)->with('orders')->get();

Structura de date rezultată este una imbricată, desigur, dar datele comenzii pot fi extrase cu ușurință. Interogarea unică rezultată, în acest caz, este cam așa:

SELECT * FROM customers INNER JOIN orders ON customers.id = orders.customer_id WHERE customers.id IN (22, 45, . . .);

O singură interogare este, desigur, mai bună decât o mie de interogări suplimentare. Imaginează-ți ce s-ar întâmpla dacă ar fi 10.000 de clienți de procesat! Sau Doamne ferește dacă am vrut să expunem și articolele conținute în fiecare comandă! Amintiți-vă, numele tehnicii este încărcare dornică și este aproape întotdeauna o idee bună.

Memorează în cache configurația!

Unul dintre motivele pentru flexibilitatea lui Laravel este multitudinea de fișiere de configurare care fac parte din cadrul. Doriți să schimbați cum/unde sunt stocate imaginile?

Ei bine, schimbați doar fișierul config/filesystems.php (cel puțin în momentul scrierii). Doriți să lucrați cu mai multe drivere de coadă? Simțiți-vă liber să le descrieți în config/queue.php. Tocmai am numărat și am descoperit că există 13 fișiere de configurare pentru diferite aspecte ale cadrului, asigurându-vă că nu veți fi dezamăgit, indiferent de ceea ce doriți să schimbați.

Având în vedere natura PHP, de fiecare dată când apare o nouă solicitare Web, Laravel se trezește, pornește totul și analizează toate aceste fișiere de configurare pentru a-și da seama cum să facă lucrurile diferit de data aceasta. Doar că e o prostie dacă nimic nu s-a schimbat în ultimele zile! Reconstruirea configurației la fiecare cerere este o risipă care poate fi (de fapt, trebuie) evitată, iar calea de ieșire este o comandă simplă pe care Laravel o oferă:

php artisan config:cache

Acest lucru face este să combine toate fișierele de configurare disponibile într-unul singur, iar memoria cache este undeva pentru o recuperare rapidă. Data viitoare când va primi o solicitare Web, Laravel va citi pur și simplu acest singur fișier și va începe.

Acestea fiind spuse, memorarea în cache a configurației este o operațiune extrem de delicată care îți poate exploda în față. Cea mai mare problemă este că, odată ce ați lansat această comandă, apelurile funcției env() de peste tot, cu excepția fișierelor de configurare, vor returna null!

Are sens când te gândești la asta. Dacă utilizați memoria cache a configurației, îi spuneți cadrului: „Știi ce, cred că am configurat lucrurile bine și sunt 100% sigur că nu vreau să se schimbe.” Cu alte cuvinte, vă așteptați ca mediul să rămână static, pentru care sunt fișierele .env.

Acestea fiind spuse, iată câteva reguli solide, sacre și de neîncălcat ale memoriei cache a configurației:

  • Fă-o doar pe un sistem de producție.
  • Fă-o doar dacă ești cu adevărat, foarte sigur că vrei să înghețe configurația.
  • În cazul în care ceva nu merge bine, anulați setarea cu php artisan cache:clear
  • Rugați-vă ca pagubele făcute afacerii să nu fie semnificative!
  • Reduceți serviciile încărcate automat

    Pentru a fi de ajutor, Laravel încarcă o mulțime de servicii când se trezește. Acestea sunt disponibile în fișierul config/app.php ca parte a cheii matricei „furnizori”. Să aruncăm o privire la ceea ce am în cazul meu:

    /*
        |--------------------------------------------------------------------------
        | Autoloaded Service Providers
        |--------------------------------------------------------------------------
        |
        | The service providers listed here will be automatically loaded on the
        | request to your application. Feel free to add your own services to
        | this array to grant expanded functionality to your applications.
        |
        */
    
        'providers' => [
    
            /*
             * Laravel Framework Service Providers...
             */
            IlluminateAuthAuthServiceProvider::class,
            IlluminateBroadcastingBroadcastServiceProvider::class,
            IlluminateBusBusServiceProvider::class,
            IlluminateCacheCacheServiceProvider::class,
            IlluminateFoundationProvidersConsoleSupportServiceProvider::class,
            IlluminateCookieCookieServiceProvider::class,
            IlluminateDatabaseDatabaseServiceProvider::class,
            IlluminateEncryptionEncryptionServiceProvider::class,
            IlluminateFilesystemFilesystemServiceProvider::class,
            IlluminateFoundationProvidersFoundationServiceProvider::class,
            IlluminateHashingHashServiceProvider::class,
            IlluminateMailMailServiceProvider::class,
            IlluminateNotificationsNotificationServiceProvider::class,
            IlluminatePaginationPaginationServiceProvider::class,
            IlluminatePipelinePipelineServiceProvider::class,
            IlluminateQueueQueueServiceProvider::class,
            IlluminateRedisRedisServiceProvider::class,
            IlluminateAuthPasswordsPasswordResetServiceProvider::class,
            IlluminateSessionSessionServiceProvider::class,
            IlluminateTranslationTranslationServiceProvider::class,
            IlluminateValidationValidationServiceProvider::class,
            IlluminateViewViewServiceProvider::class,
    
            /*
             * Package Service Providers...
             */
    
            /*
             * Application Service Providers...
             */
            AppProvidersAppServiceProvider::class,
            AppProvidersAuthServiceProvider::class,
            // AppProvidersBroadcastServiceProvider::class,
            AppProvidersEventServiceProvider::class,
            AppProvidersRouteServiceProvider::class,
    
        ],

    Încă o dată, am numărat și sunt listate 27 de servicii! Acum, este posibil să aveți nevoie de toate, dar este puțin probabil.

    De exemplu, se întâmplă să construiesc un API REST în acest moment, ceea ce înseamnă că nu am nevoie de furnizorul de servicii de sesiune, furnizorul de servicii de vizualizare etc. Și din moment ce fac câteva lucruri în felul meu și nu urmez setările implicite ale cadrului , pot dezactiva și Furnizorul de servicii de autentificare, Furnizorul de servicii de paginare, Furnizorul de servicii de traducere și așa mai departe. Per total, aproape jumătate dintre acestea nu sunt necesare pentru cazul meu de utilizare.

      Pe care să alegi în 2023

    Aruncă o privire lungă și serioasă la cererea ta. Are nevoie de toți acești furnizori de servicii? Dar, pentru numele lui Dumnezeu, vă rugăm să nu comentați orbește aceste servicii și să treceți la producție! Rulați toate testele, verificați manual lucrurile pe mașinile de dezvoltare și de pregătire și fiți foarte, foarte paranoici înainte de a apăsa pe trăgaci. 🙂

    Fiți înțelept cu stivele de middleware

    Când aveți nevoie de o procesare personalizată a cererii Web primite, crearea unui nou middleware este răspunsul. Acum, este tentant să deschideți aplicația/Http/Kernel.php și să puneți middleware-ul în stiva web sau API; în acest fel, devine disponibil în aplicație și dacă nu face ceva intruziv (cum ar fi înregistrarea sau notificarea, de exemplu).

    Cu toate acestea, pe măsură ce aplicația crește, această colecție de middleware global poate deveni o povară tăcută pentru aplicație dacă toate (sau majoritatea) sunt prezente în fiecare solicitare, chiar dacă nu există niciun motiv de afaceri pentru asta.

    Cu alte cuvinte, aveți grijă unde adăugați/aplicați un nou middleware. Poate fi mai convenabil să adăugați ceva la nivel global, dar penalizarea performanței este foarte mare pe termen lung. Știu durerea pe care ar trebui să o suferi dacă ai aplica în mod selectiv middleware de fiecare dată când apare o nouă schimbare, dar este o durere pe care aș accepta și o recomand de bunăvoie!

    Evitați ORM (uneori)

    În timp ce Eloquent face multe aspecte ale interacțiunii DB plăcute, aceasta are prețul vitezei. Fiind un mapator, ORM nu numai că trebuie să preia înregistrări din baza de date, ci și să instanțieze obiectele model și să le hidrateze (le completează) cu date de coloană.

    Deci, dacă faceți un simplu $users = User::all() și există, să zicem, 10.000 de utilizatori, cadrul va prelua 10.000 de rânduri din baza de date și va face intern 10.000 de noi User() și va completa proprietățile lor cu datele relevante. . Acestea sunt cantități masive de muncă în spatele scenei, iar dacă baza de date este locul în care aplicația dvs. devine un blocaj, ocolirea ORM este o idee bună uneori.

    Acest lucru este valabil mai ales pentru interogările SQL complexe, în care ar trebui să săriți o mulțime de cercuri și să scrieți închideri peste închideri și totuși să ajungeți la o interogare eficientă. În astfel de cazuri, este de preferat să faceți un DB::raw() și să scrieți manual interogarea.

    Merge de acest studiu de performanță, chiar și pentru inserții simple Eloquent este mult mai lent pe măsură ce crește numărul de înregistrări:

    Utilizați stocarea în cache cât mai mult posibil

    Unul dintre secretele cel mai bine păstrate ale optimizării aplicațiilor web este stocarea în cache.

    Pentru cei neinițiați, stocarea în cache înseamnă precalcularea și stocarea rezultatelor costisitoare (costisitoare în ceea ce privește utilizarea CPU și a memoriei) și pur și simplu returnarea lor atunci când se repetă aceeași interogare.

    De exemplu, într-un magazin de comerț electronic, s-ar putea întâlni cu cel din cele 2 milioane de produse, de cele mai multe ori oamenii sunt interesați de cele care sunt proaspăt stocate, într-un anumit interval de preț și pentru o anumită grupă de vârstă. Interogarea bazei de date pentru aceste informații este o risipă – deoarece interogarea nu se schimbă des, este mai bine să stocăm aceste rezultate într-un loc unde să putem accesa rapid.

    Laravel are suport încorporat pentru mai multe tipuri de stocarea în cache. Pe lângă utilizarea unui driver de stocare în cache și construirea sistemului de stocare în cache de la zero, este posibil să doriți să utilizați câteva pachete Laravel care facilitează stocarea în cache a modelului, interogarea în cacheetc.

    Dar rețineți că dincolo de un anumit caz de utilizare simplificat, pachetele de cache prefabricate pot cauza mai multe probleme decât rezolvă.

    Preferați stocarea în cache în memorie

    Când memorați ceva în cache în Laravel, aveți mai multe opțiuni de unde să stocați calculul rezultat care trebuie să fie stocat în cache. Aceste opțiuni sunt cunoscute și ca drivere de cache. Deci, deși este posibil și perfect rezonabil să folosiți sistemul de fișiere pentru stocarea rezultatelor cache-ului, nu este cu adevărat ceea ce ar trebui să fie memorarea în cache.

    În mod ideal, doriți să utilizați un cache în memorie (care trăiește în întregime în RAM), cum ar fi Redis, Memcached, MongoDB etc., astfel încât, la sarcini mai mari, memoria cache să servească o utilizare vitală, mai degrabă decât să devină un blocaj în sine.

    Acum, ați putea crede că a avea un disc SSD este aproape la fel cu a folosi un stick RAM, dar nu este nici măcar aproape. Chiar și informal repere arată că memoria RAM depășește SSD-ul de 10-20 de ori când vine vorba de viteză.

    Sistemul meu preferat când vine vorba de cache este Redis. este ridicol de repede (100.000 de operații de citire pe secundă sunt obișnuite), iar pentru sistemele cache foarte mari, pot fi evoluate într-un cluster uşor.

      Cum să umpleți textul cu o imagine în Photoshop

    Memorați în cache traseele

    La fel ca și configurația aplicației, rutele nu se schimbă prea mult în timp și sunt un candidat ideal pentru cache. Acest lucru este valabil mai ales dacă nu suportați fișiere mari ca mine și ajungeți să vă împărțiți web.php și api.php pe mai multe fișiere. O singură comandă Laravel împachetează toate rutele disponibile și le ține la îndemână pentru acces viitor:

    php artisan route:cache

    Și când ajungi să adaugi sau să schimbi rute, pur și simplu faci:

    php artisan route:clear

    Optimizare imagine și CDN

    Imaginile sunt inima și sufletul majorității aplicațiilor Web. Întâmplător, ei sunt, de asemenea, cei mai mari consumatori de lățime de bandă și unul dintre cele mai mari motive pentru aplicații/site-uri web lente. Dacă pur și simplu stocați imaginile încărcate în mod naiv pe server și le trimiteți înapoi în răspunsuri HTTP, lăsați să treacă o oportunitate masivă de optimizare.

    Prima mea recomandare este să nu stocați imaginile la nivel local – există problema pierderii de date cu care trebuie să o faceți și, în funcție de regiunea geografică în care se află clientul dvs., transferul de date poate fi dureros de lent.

    În schimb, alege o soluție de genul Cloudinary care redimensionează și optimizează automat imaginile din mers.

    Dacă acest lucru nu este posibil, utilizați ceva de genul Cloudflare pentru a stoca în cache și a difuza imagini în timp ce acestea sunt stocate pe serverul dvs.

    Și dacă chiar și acest lucru nu este posibil, modificarea puțin software-ul serverului web pentru a comprima activele și a direcționa browserul vizitatorului să memoreze lucrurile în cache, face o mare diferență. Iată cum ar arăta un fragment din configurația Nginx:

    server {
    
       # file truncated
        
        # gzip compression settings
        gzip on;
        gzip_comp_level 5;
        gzip_min_length 256;
        gzip_proxied any;
        gzip_vary on;
    
       # browser cache control
       location ~* .(ico|css|js|gif|jpeg|jpg|png|woff|ttf|otf|svg|woff2|eot)$ {
             expires 1d;
             access_log off;
             add_header Pragma public;
             add_header Cache-Control "public, max-age=86400";
        }
    }

    Sunt conștient că optimizarea imaginii nu are nimic de-a face cu Laravel, dar este un truc atât de simplu și puternic (și este atât de des neglijat) care nu m-a putut abține.

    Optimizare autoloader

    Încărcarea automată este o caracteristică îngrijită, nu atât de veche în PHP, care, probabil, a salvat limbajul de la nenorocire. Acestea fiind spuse, procesul de găsire și încărcare a clasei relevante prin descifrarea unui șir de spațiu de nume dat necesită timp și poate fi evitat în implementările de producție unde este de dorit o performanță ridicată. Încă o dată, Laravel are o soluție cu o singură comandă:

    composer install --optimize-autoloader --no-dev

    Fă-ți prieteni cu cozile

    Cozile sunt modul în care procesați lucrurile atunci când sunt multe dintre ele, iar fiecare dintre ele durează câteva milisecunde pentru a se finaliza. Un exemplu bun este trimiterea de e-mailuri – un caz de utilizare larg răspândit în aplicațiile web este de a lansa câteva e-mailuri de notificare atunci când un utilizator efectuează anumite acțiuni.

    De exemplu, într-un produs nou lansat, este posibil să doriți ca conducerea companiei (unele 6-7 adrese de e-mail) să fie notificată ori de câte ori cineva plasează o comandă peste o anumită valoare. Presupunând că gateway-ul dvs. de e-mail poate răspunde la cererea dvs. SMTP în 500 ms, vorbim de o așteptare bună de 3-4 secunde pentru utilizator înainte de a începe confirmarea comenzii. O parte foarte proastă de UX, sunt sigur că veți de acord.

    Remediul este să stocați joburile pe măsură ce intră, să spuneți utilizatorului că totul a mers bine și să le procesați (câteva secunde) mai târziu. Dacă există o eroare, joburile aflate în coadă pot fi reîncercate de câteva ori înainte ca acestea să fie declarate a eșuat.

    Credite: Microsoft.com

    În timp ce un sistem de așteptare complică puțin configurarea (și adaugă puțină suprasarcină de monitorizare), este indispensabil într-o aplicație web modernă.

    Optimizarea activelor (Laravel Mix)

    Pentru orice material front-end din aplicația dvs. Laravel, vă rugăm să vă asigurați că există o conductă care compilează și minimizează toate fișierele de active. Cei care se simt confortabil cu un sistem de bundle precum Webpack, Gulp, Parcel etc., nu trebuie să se deranjeze, dar dacă nu faci asta deja, Laravel Mix este o recomandare solidă.

    Mix este un pachet ușor (și încântător, cu toată sinceritatea!) în jurul Webpack, care are grijă de toate fișierele dvs. CSS, SASS, JS etc. pentru producție. Un fișier .mix.js tipic poate fi la fel de mic și poate face minuni:

    const mix = require('laravel-mix');
    
    mix.js('resources/js/app.js', 'public/js')
        .sass('resources/sass/app.scss', 'public/css');

    Acest lucru se ocupă automat de importuri, minimizare, optimizare și întregul shebang atunci când sunteți gata pentru producție și rulați producția npm run. Mix are grijă nu doar de fișierele tradiționale JS și CSS, ci și de componentele Vue și React pe care le puteți avea în fluxul de lucru al aplicației.

    Mai multe informatii Aici!

    Concluzie

    Optimizarea performanței este mai mult artă decât știință – a ști cum și cât de mult să faci este important decât ce să faci. Acestea fiind spuse, nu există sfârșit cât și ce puteți optimiza într-o aplicație Laravel.

    Dar orice ai face, aș dori să-ți las un sfat de despărțire — optimizarea ar trebui făcută atunci când există un motiv solid și nu pentru că sună bine sau pentru că ești paranoic în ceea ce privește performanța aplicației pentru peste 100.000 de utilizatori în realitate. sunt doar 10.

    Dacă nu ești sigur dacă trebuie să-ți optimizezi sau nu aplicația, nu trebuie să dai cu piciorul în cuibul proverbial de viispe. O aplicație care funcționează, care se simte plictisitoare, dar care face exact ceea ce trebuie, este de zece ori mai de dorit decât o aplicație care a fost optimizată într-o supermașină hibridă mutantă, dar care cade din când în când.

    Și, pentru ca începătorul să devină un maestru Laravel, verifică asta curs online.

    Fie ca aplicațiile dvs. să ruleze mult, mult mai repede! 🙂