Olen työni puolesta tehnyt webkehitystä 12 vuotta ja harrastusmielessä pari vuotta enemmän.
Periaatteessa kaikki web-sovellukset ovat (nykyään) samanlaisia; spa-frontti, crud(create,read,update,delete)-rajapintoja sekä muutama integraatio.
Joskus samaa rajapintaa käytetään myös mm. mobiilisovelluksissa.
Peruslaiskana ihmisenä minulla on pieni fiksaatio tuottavuuteen.
Olen aina valmis muuttamaan tapojani ja ohjelmoinnin kohdalla työkalujani, jos sillä tavoin pääsen pienemmällä vaivalla samaan lopputulokseen (tai saan samalla vaivalla enemmän valmista).
Päätin tutkia kunnolla mitä teknologioita on tarjolla ja onko oikeastaan väliä millä tekee. Löytyisikö framework, jolla saisi tehtyä saman kuin millä tahansa muullakin, mutta vähän helpommin?
Milloin ohjelmointi on vaivatonta ja tuottavaa?
Toki noiden lisäksi on mm. oppimiseen, ylläpidettävyyteen sekä makuasioihin liittyviä tekijöitä. Olen nyt kuitenkin suosion, toiminnallisuuksien ja elinvoimaisuuden näkökulmasta tonkinut bäkkäri-frameworkeja pari kuukautta, joten tässä löydöksiä tähän mennessä.
Käytin seuraavia lähteitä teknologioiden suosion vertailuun
Yhdistämällä em. tilastot voidaan ohjelmointikielet laittaa suosituimmuusjärjestykseen
Kieli | Tiobe | PyPL | Stackshare | Stack overflow | SH Framework |
---|---|---|---|---|---|
Python | 3 | 1 | 2 | 4 | 1 (Django) |
JavaScript | 7 | 3 | 1 | 1 | 4 (Express) |
Java | 2 | 2 | 4 | 5 | 6 (Spring) |
C# | 5 | 4 | 6 | 7 | 2 (ASP.NET) |
PHP | 8 | 6 | 3 | 9 | 3 (Laravel) |
C | 1 | 5 | 17 | 11 | |
C++ | 4 | 5 | 15 | 10 | |
R | 9 | 7 | 17 | ||
Objective-C | 18 | 8 | 14 | 20 | |
Swift | 16 | 9 | 13 | 16 | |
TypeScript | 46 | 10 | 5 | 8 | 4 (Express) |
Matlab | 15 | 11 | |||
Kotlin | 33 | 12 | 18 | 13 | 6 (Spring) |
Go | 14 | 13 | 12 | 12 | |
Ruby | 13 | 15 | 7 | 14 | 5 (Rails) |
VBA | 14 | 15 | |||
Rust | 25 | 16 | 8 | 19 | |
Scala | 21 | 17 | 16 | 21 | |
Visual Basic | 6 | 18 | |||
Dart | 26 | 21 | 10 | 22 |
Vain harvalle ohjelmointikielelle löytyy suosittu web framework, joten top 5 bäkkärikieliksi on helppo saada Python, JavaScript/TypeScript, Java, C# ja PHP.
Frameworkin ei tarvitse kuitenkaan olla se kaikkein suosituin niinpä seuraavaksi etsin valituille ohjelmointikielille muitakin bäkkäri-frameworkeja Stacksharesta sekä nettihauilla. Katsoin frameworkeille Stackshare-käyttäjien lukumäärän, viimeisimmän julkaisupäivän, githubin contributors-lukeman sekä Stack overflow:sta tägättyjen kysymysten määrän. Jätin jäljelle ne, jotka
Tarkempaan vertailuun jäi seuraavat 29 frameworkia (pudotetut yliviivattu)
Python:
AIOHTTP, Django, Django REST Framework, Falcon, FastAPI, Flask, Tornado
Apistar, Bottle, Cornice, Eve, Flask Api, Flask restful, Guillotina,
Hug, Sandman2, Sanic, Tastypie, Turbogears, Vibora, Web2py
Java:
Akka HTTP, Dropwizard, Micronaut, Play framework, Quarkus, Spring, Vert.x
CRNK, Elide, Javalin, Jersey, Jooby, Micro-server, Rapidoid, Ratpack,
Rest.li, RestEasy, RestExpress, Restlet, RestX, Spark Java, Spincast
JavaScript/TypeScript:
Express, Hapi, Feathers, Koa, NestJS, Sails
Actionhero.js, AdonisJS, DerbyJS, Fastify, Huncwot, KeystoneJS, LoopBack,
Meteor, Moleculer, QEWD.js, Restify, ThinkJS, Tinyhttp, Total.js
C#:
ASP.NET Core, ServiceStack
ASP.NET, Carter, LightNode, Nancy, Restier
PHP:
CakePHP, CodeIgniter, Laravel, Phalcon, Slim, Symfony
API platform, Laminas, Lumen, Nette, PSX, Silex, Spiral, Waterpipe, Yii
Frameworkien dokumentaatioita selaamalla kokosin 50 yleistä toiminnallisuutta. Jätin pois sellaisia mitä en koe nykyään juuri tarvittavan spa-clientin kanssa (esim. cookiet, sessiot ja template enginet). Arvioin työläysasteen näille toiminnallisuuksille kaikilla frameworkeilla virallisesta dokumentaatiosta ja nettihauilla.
Käytin seuraavaa pisteytystä
1 = copy-paste ratkaisu löytyy suoraan dokumentaatiosta
2 = copy-paste ratkaisu löytyy netistä / valmis kirjasto
3 = ei löydy suoraa vastausta, mutta hakutuloksia / kirjastoja yhdistelemällä onnistunee (= jotain sinne päin)
4 = ei relevantteja hakutuloksia (katsoin ensimmäiset ~10)
RESTful routing (method + path + parameters)
// Express
app.get('/products/:id(\\d+)', function (req, res) {
// ...
})
Reverse routing (urlin generointi reitin nimellä + parametreilla)
# Django
urlpatterns = [
path('products/<int:id>/', views.product_details, name='product-details'),
]
id = 123
url = reverse('product-details', args=(id))
Semantic versioning (headerissä arvo mitä versiota client haluaa käyttää)
// Restify
server.get('/products/:id', restify.plugins.conditionalHandler([
{ version: '1.1.0', handler: getProductByIdV1 },
{ version: '2.0.0', handler: getProductByIdV2 }
]));
Subdomain routing
// Laravel
Route::domain('{client}.example.com')->group(function () {
Route::get('products', function ($client) {
// ...
});
});
Static files (tiedostot hakemistossa /x tarjotaan polusta /y)
// ASP.NET Core
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider("path/to/files"),
RequestPath = "/assets"
});
Rate limiting (esim. x kutsua minuutissa)
// Laravel
RateLimiter::for('global', function (Request $request) {
return Limit::perMinute(1000);
});
Route::middleware(['throttle:global'])->group(function () {
Route::get('/products', function () {
//
});
});
Middleware: Decorate request, terminate, filter, modify response (muokkaa requestia/responsea)
# AIOHTTP
@middleware
async def example_middleware(request, handler):
# before / filter / decorate / terminate
resp = await handler(request)
# after
return resp
Middleware conventions (määrittele joustavasti missä middlewarea käytetään)
// ASP.NET Core
[ExampleFilter] // apply to all sub classes
public abstract class ExampleControllerBase : ControllerBase {}
[ExampleFilter] // apply to all actions
public class ExampleController : ControllerBase
{
[ExampleFilter] // apply to single action
[HttpGet("")]
public IActionResult SomeAction() {}
}
Content-negotiation (konvertoi json/xml headereitten mukaan)
// ASP.NET Core
// Add XML support
services.AddControllers()
.AddXmlSerializerFormatters();
Dependency injection
// NestJS
export class ProductsController {
// service is injected
constructor(private productService: ProductService) {}
}
Model binding
// NestJS
@Put(':id')
updateProduct(@Param('id') id: string, @Body() product: ProductModel) {
// ...
}
Validation (api heittää virheen, jos ei ole validi)
// NestJS
export class ProductModel {
@IsNotEmpty()
name: string;
// ...
}
Caching (esim apin tulos cachetetaan joksikin aikaa)
// ASP.NET Core
public class NewsController : Controller
{
[HttpGet]
[ResponseCache(VaryByQueryKeys = new[] { "category" }, Duration = 300)]
public NewsModel GetMostReadNews(string category)
{
// ...
}
}
Auth:
Username+password, Google/Facebook/..., roles, jwt, policies, conventions
OpenAPI (apien dokumentointi)
Messaging:
Broadcasting (websocket), events
Task scheduling
// NestJS
@Injectable()
export class TasksService {
@Cron('0 */5 * * * *')
handleCron() {
// ...
}
}
GraphQL:
queries, resolvers, mutations, subscriptions
Lähes kaikissa frameworkeissa oli joitakin toiminnallisuuksia, joihin ei löytynyt ratkaisua tai ne olivat työläitä (kts. kuvan tummat pystypalkit).
Yleisimmät ongelmat
10. Symfony 98p (1.96p/toiminnallisuus)
Juurikin em. suurimmat puutteet. Lisäksi GraphQL erityisen työläs.
9. Quarkus 96p (1.92p/toiminnallisuus)
Semanttinen versiointi puuttuu. Autentikaatio työläs some-tileillä.
9. Django 96p (1.92p/toiminnallisuus)
Model bindingin puutteesta aiheutuu toistuvaa työtä.
View-ajatusmalli sopii huonosti RESTful-routeihin.
7. Django REST framework 95p (1.9p/toiminnallisuus)
Samat kuin perus Djangossa.
6. ServiceStack 84p (1.68p/toiminnallisuus)
Semanttinen versiointi puuttuu ja GraphQL-tuesta ei löydy mitään.
GraphQLn takia ei pääse top5:een.
Dokumentaatio oli esimerkillinen. Hyviä copy-paste -esimerkkejä moneen toiminnallisuuteen. Suurin ongelma Laravelissa on vahvasti tyypitettävien requestin ja responsen puute. Tästä aiheutuu toistuvaa validointilogiikkaa. Lisäksi OpenAPI-kuvauksiin pitää toistaa uudestaan sama schema, joka on jo validointia varten määritelty.
80p (1.6p/toiminnallisuus)
Hyvää
Huonoa
Pisteitään paremman sijoituksen Spring ansaitsee runsailla toiminnallisuuksillaan. Mitään ei jää tekemättä. Ratkaisut tosin löytyivät useammin baeldung.com sivustolta kuin virallisesta dokumentaatiosta mikä saattaa olla ongelma, jos ko. sivustoa ei päivitetä.
91p (1.82p/toiminnallisuus)
Hyvää
Huonoa
Mukavan minimaalinen lähestymistapa. Suurin osa toiminnallisuuksista helppo toteuttaa. Valitettavasti osaan (harvemmin käytettyjä?) toiminnallisuuksia ei löytynyt ratkaisua.
87p (1.74p/toiminnallisuus)
Hyvää
Huonoa
NestJS:n dokumentaatio oli hyvin selkeä ja toiminnallisuuksia löytyi mukavasti. ASP.NET Core puolestaan oli ainoa framework, jolle kaikkiin toiminnallisuuksiin löytyi helppo ratkaisu.
Näiden paremmuus riippuu aika paljon kielen (TypeScript/C#) ja alustan (Node/.NET Core) hyödynnettävyydestä.
72p (1.44p/toiminnallisuus)
Hyvää
Huonoa
Vaihtoehto #1 jos
60p (1.2p/toiminnallisuus)
Hyvää
Huonoa
Vaihtoehto #1 jos