Welche Sprache für Backend-Serverentwicklung?

Dawosch

Neues Mitglied
Moin zusammen,
ich überlege seit kurzem mir eine neue Sprache anzueignen.
Jetzt habe ich die Qual der Wahl und überlege, was da für mich in Frage kommt und habe gehofft, von euch ein paar richtungsweisende Tipps bekommen zu können.

Daher aber zuerst einmal, wie meine Ausgangslage ist.
Ich bin großer Fan von JavaScript (Typescript um genau zu sein) mit jahrelanger Erfahrung in der Webentwicklung. Vorzugsweise mit React im Frontend und NodeJS (ExpressJS) im Backend.
Man kann schon viel mit ExpressJS machen, aber so richtig geilen Code im Sinne der Performance kann man damit nicht schreiben.
Daher meine Idee, genau diesen Gap mit einer anderen Sprache zu verbessern.

Welche Sprachen schließe ich im Vorhinein aus?
Ich habe schon einige Sprachen ausprobiert, weshalb ich diese daher bereits am Anfang ausschließen würde.
  • Java
  • PHP
  • Ruby
  • .NET

Was wäre meine Traumvorstellung?
Am liebsten wäre mir eine Sprache, welche folgende Voraussetzungen erfüllt. Wobei ich natürlich abstriche mache und kompromissbereit bin.
Die Auflistung soll vielmehr euch zeigen, was ich mir grob vorstelle.
Die Nummerierung gibt an, wie wichtig mir der Punkt ist (je kleiner die Zahl, deste wichtiger)

  1. Multi-Plattform
  2. Füs Web geeignet (Entwicklung von Rest-Schnittstellen, Authentication, ...)
  3. Javascript/Typescript ähnliche Syntax
  4. Performance
  5. Starke Community

Wie ihr erkennen könnt, möchte ich primär eine Sprache, mit der ich Rest-Schnittstellen fürs Web entwickeln kann.


Postet also gerne eure Erfahrung, gerne auch mit einem Code-Snippet, damit man sich direkt ein Bild von der Syntax machen kann.
Und natürlich, warum ihr die Sprache für diesen Anwendungsfall für geeignet haltet.


PS:
Ich habe mir natürlich schon Sprachen angesehen, möchte aber vorab keine Infos geben, um euch nicht zu beeinflussen.
Man liest ja sehr viel, warum welche Sprache besser ist als andere, aber in wie fern diese Einschätzungen Praxisrelevant sind, ist oft einmal etwas anderes.
Ich freue mich auf eure Kommentare.
 
Ich würde sagen, dass kommt stark auf deinen Anwendungsfall an. Wenn dir ähnliche Syntax wie JavaScript / TypeScript sehr wichtig ist, bist du mit Java bestimmt am besten beraten. Wenn du unbedingt krasse Performance brauchst, wäre sicherlich etwas Richtung C besser. Ich persönlich mag es gerne, das Backend in Python zu entwickeln und Dinge, die dann performancekritisch sind in C zu schreiben (wobei ich das noch nie wirklich tun musste - die Performance von Python hat in meinen Anwendungsfällen stets ausgereicht).
Zusätzlich zur Programmiersprache gibt es natürlich auch alle möglichen Frameworks. Im folgenden stelle ich dir mal kurz den Techstack vor, den wir momentan bei meiner Arbeit verwenden:
Backend ist komplett in Python. Für die Erstellung der Schnittstelle verwenden wir FastAPI. Für die Kommunikation mit der Datenbank SQLAlchemy. Für eine saubere Datenbankmigration verwenden wir zusätzlich Alembic. Für Authentifizierung und Autorisierung + Rechte Management benutzen wir Auth0 und binden dies in unser Backend ein.
Die Performance ist ganz gut. Wenn dir das aus irgendeinem Grund noch nicht ausreichen sollte, kannst du auch performancekritischen Code auslagern, indem du diesen in C schreibst, kompilierst und dann in deinen Python Code einbindest.
Die Syntax ist natürlich recht unterschiedlich, aber nichts woran man sich nicht gewöhnen könnte ^^ Ich zeig dir mal ein kleines Beispiel aus unserm Backend (vereinfacht copy & paste):
Beispiel unserer REST Endpunkte:
router = APIRouter()


@router.post(
    "/",
    response_model=schemas.Marktteilnehmer,
    status_code=status.HTTP_201_CREATED,
)
def create_marktteilnehmer(
    *,
    db: Session = Security(deps.db_conn, scopes=["read:marktteilnehmer", "create:marktteilnehmer"]),
    marktteilnehmer_in: schemas.MarktteilnehmerCreate,
) -> Any:
    """
    Create new marktteilnehmer.
    """
    # ...


@router.get("/", response_model=List[schemas.Marktteilnehmer])
def read_marktteilnehmer(
    db: Session = Security(deps.db_conn, scopes=["read:marktteilnehmer"]),
    pagination: MarktteilnehmerPagination = Depends(),
) -> Any:
    """
    Retrieve marktteilnehmer
    The list is sorted in ascending order by creation date.
    """
    # ...


@router.get("/by_id/{marktteilnehmer_id}", response_model=schemas.Marktteilnehmer)
def read_marktteilnehmer_by_id(
    marktteilnehmer_id: UUID, db: Session = Security(deps.db_conn, scopes=["read:marktteilnehmer"])
) -> Any:
    """
    Get marktteilnehmer by its id (Database Primary Key)
    """
    # ...


@router.put("/{marktteilnehmer_id}", response_model=schemas.Marktteilnehmer)
def update_marktteilnehmer(
    *,
    db: Session = Security(deps.db_conn, scopes=["read:marktteilnehmer", "update:marktteilnehmer"]),
    marktteilnehmer_id: UUID,
    marktteilnehmer_in: schemas.MarktteilnehmerUpdate,
) -> Any:
    """
    Update existing marktteilnehmer
    """
    # ...


@router.delete("/{marktteilnehmer_id}", response_model=schemas.Marktteilnehmer)
def delete_marktteilnehmer(
    *,
    db: Session = Security(deps.db_conn, scopes=["read:marktteilnehmer", "delete:marktteilnehmer"]),
    marktteilnehmer_id: UUID,
) -> Any:
    """
    Delete existing marktteilnehmer
    """
    # ...
Ausschnitt aus unseren FastAPI deps:
async def security(
    security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme), tenant: Tenant = Depends(get_tenant)
) -> TokenData:
    """
    Validates the token and checks if it has enough permissions to access the endpoint. Additionally, checks if the
    token has permission to access the current tenant (determined by `get_tenant`). Returns the unpacked payload of
    the token.
    """
    token_data, authentication_value = await get_token_data(security_scopes, token)
    security_scopes.scopes.append(f"tenant:{tenant.db_schema}")
    await check_permissions(security_scopes, token_data, authentication_value)
    return token_data


async def db_conn(tenant: Tenant = Depends(get_tenant), _=Security(security)) -> AsyncGenerator[Session, None]:
    """
    After security check, connects to database with the tenant schema determined by the subdomain. All tenant related
    requests can only access this tenant schema and only if you have the required permissions defined by the JWT.
    """
    with with_db(tenant.db_schema) as db:
        yield db
Beispiel für CRUD Operationen mittels SQLAlchemy:
class CRUDBase(Generic[ModelT, CreateSchemaT, UpdateSchemaT, PaginationT, FiltersT, SortCriteriaT]):
    """
    Base class to collect all basic crud operations
    """

    def __init__(self):
        """
        Initializes the CRUD class. `self.model` as property is a little workaround
        to get the model from the generic argument inside the python code.
        """
        self._model: Optional[type[ModelT]] = None

    @property
    def model(self) -> type[ModelT]:
        """
        Holds the model type (Contains the same value as ModelT).
        """
        if self._model is None:
            if not hasattr(self, "__orig_class__"):
                raise TypeError("You probably forgot to define the generic type of your CRUD-class.")
            # pylint: disable=no-member
            self._model = self.__orig_class__.__args__[0]  # type:ignore[attr-defined]
        return self._model

    def get(self, db: Session, model_id: Any) -> Optional[ModelT]:
        """
        Get specific entity by model_id from database
        model_id corresponds to the id column in the database
        """
        return db.scalars(select(self.model).where(self.model.id == model_id)).one_or_none()

Ist wie gesagt nur ein Auszug, aber vielleicht gibt es dir ein Gefühl davon, ob das was für dich wäre :)
 
Ich kann selbst keine Erfahrungen in den folgenden Vorschlägen vorweisen, aber das ist mir spontan in den Sinn gekommen:

Python finde ich naheliegend, vor allem weil es da auch gute Webframeworks gibt und man Python auch universell einsetzen kann.

Den Vorschlag von @lord_haffi mit C finde ich ehrlich gesagt interessant, wobei ich mir C schlecht für das Web vorstellen kann. Ist wahrscheinlich auch mehr in Verbindung mit Python gemeint, weniger um eine ganze Web App zu schreiben.

Ansonsten sind übliche Verdächtige wahrscheinlich noch Go und Rust.
Zu Go kann dir sicher @dominik was sagen.


Ich persönlich würde, wenn Java, PHP und Co wegfallen, auf jeden Fall zu Python tendieren.
 
wobei ich mir C schlecht für das Web vorstellen kann. Ist wahrscheinlich auch mehr in Verbindung mit Python gemeint, weniger um eine ganze Web App zu schreiben.
Genau :) Es gibt halt selten Fälle, in denen du in der Webentwicklung mehr Performance brauchst als Python liefern kann. Und für diese Fälle, kann man das dann halt sehr gut und einfach in Python einbinden. Ob man n ganzes Backend in C schreiben will, wag ich zu bezweifeln :D
 
Vielen Dank für eure Antworten.
An Python habe ich auch schon gedacht und auch ein paar Gehversuche unternommen, aber so ganz bin ich da nicht rein gekommen.
Aber da werde ich evtl. nochmal näher schauen.

Mit C und Performance habt ihr natürlich recht, aber da sehe ich mich im Jahre 2023 für meine Anwendungsfälle eher nicht mehr :)
Mit Java arbeite ich schon seit jahren, deswegen hatte ich Java raus genommen.
Ich habe aber auch vergessen zu sagen, dass meine Anwendungen oftmals auf kleinen Computern wie Raspberry Pis laufen sollen.
Ein Fakt, den ich evtl. hätte erwähnen sollen :)

Was Rust und Go angehet, wäre ich wirklich mal auf Erfahrungswerte gespannt.
Wobei mir die Syntax bei Rust im ersten Moment nicht zusagt.
Wie gesagt, ich arbeite viel mit Javascript, da sind Sprachen bei denen es Pointer gibt immer erschreckend xD

Wirklich schon einmal vielen Dank.


PS:
Ich habe mir eben mal FastAPI angesehen, da ich bisher nur Flask kannte.
Aber die Aussage auf deren Seite finde ich amüsant.

1678994524303.png

Dann sollte ich evtl. doch bei NodeJS (ExpressJS) bleiben, wenn Python on par mit NodeJS ist xD
 
Was Rust und Go angehet, wäre ich wirklich mal auf Erfahrungswerte gespannt.
Wobei mir die Syntax bei Rust im ersten Moment nicht zusagt.
Wie gesagt, ich arbeite viel mit Javascript, da sind Sprachen bei denen es Pointer gibt immer erschreckend xD

Ja, geht mir ähnlich. :D

Ich hatte übrigens mal ChatGPT befragt und folgende Antwort bekommen (Ausschnitt):

Go ist eine Sprache, die eine schnelle Kompilierung, eine einfache Syntax und eine starke Typisierung bietet. Es ist auch bekannt für seine gute Unterstützung für das gleichzeitige Ausführen von Code (Concurrency) und ist daher besonders geeignet für Anwendungen mit vielen gleichzeitigen Anfragen. Hier ist ein Beispielcode, der einen einfachen RESTful-Service in Go implementiert:

Go:
package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

type Todo struct {
	ID        string `json:"id,omitempty"`
	Name      string `json:"name,omitempty"`
	Completed bool   `json:"completed,omitempty"`
}

var todos []Todo

func GetTodos(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode(todos)
}

func main() {
	router := mux.NewRouter()

	todos = append(todos, Todo{ID: "1", Name: "Buy groceries", Completed: false})
	todos = append(todos, Todo{ID: "2", Name: "Do laundry", Completed: false})

	router.HandleFunc("/todos", GetTodos).Methods("GET")

	log.Fatal(http.ListenAndServe(":8000", router))
}

Rust ist eine Sprache, die auf Leistung, Sicherheit und Geschwindigkeit ausgelegt ist. Sie bietet eine sehr strenge Typisierung und eine leistungsstarke Garbage Collection. Rust hat auch eine starke Gemeinschaft, die das Wachstum der Sprache und die Entwicklung von Bibliotheken und Frameworks vorantreibt. Hier ist ein Beispielcode, der einen einfachen RESTful-Service in Rust implementiert:

Code:
use std::io::{self, Read};
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Todo {
    id: String,
    name: String,
    completed: bool,
}

async fn get_todos(_req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    let mut todos = Vec::new();
    todos.push(Todo { id: "1".to_string(), name: "Buy groceries".to_string(), completed: false });
    todos.push(Todo { id: "2".to_string(), name: "Do laundry".to_string(), completed: false });
    let body = Body::from(serde_json::to_string(&todos).unwrap());
    Ok(Response::new(body))
}

#[tokio::main]
async fn main() {
    let make_svc = make_service_fn(|_conn| async {
        Ok::<_, hyper::Error>(service_fn(get_todos))
    });

    let addr = ([127, 0, 0, 1], 8000).into();
    let server = Server::bind(&addr).serve(make_svc);

    println!("Listening on http://{}", addr);

    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}

So bekommst du schon mal einen kleinen Eindruck der Syntax und wie eine sehr vereinfachte REST-API aussehen könnte.

Für mich sieht Rust etwas komplizierter und schwerer zu lesen aus, das kann aber auch am Fallbeispiel liegen. Gewisse Ähnlichkeiten mit JS hat es ja stellenweise aber schon.

ChatGPT hat allerdings soweit ich das sehe bei Rust schon mal einen Fehler eingebaut, indem es Rust eine Garbage Collection angedichtet hat.

Ein großes Manko ist natürlich die Tatsache, dass es hier im Forum kein Syntax Highlighting für Rust gibt. :D
 
Ich verstehe dein Posting so, dass du eine performante JS-Alternative suchst. Aber dann ist Performanz bei dir erst auf Platz 4. :p

Ich nehme mal an, du hast eine Liste mit Kandidaten. Gehe doch einfach mal alle durch, die du nicht direkt ausgeschlossen hast. Es dauert ja nicht lang, sich einen Überblick über ein Stack zu verschaffen. Kannst ja mehrere Kandidaten ausprobieren, auch wenn dir die Syntax oder Beschreibung erstmal nicht zusagen. Und wenn dir gefällt, was du siehst, kannst du da genauer rein schauen.

Aber ggf. hat ja noch jemand eine komplett andere Meinung wie z.B. Julia oder Erlang.

Vielleicht meintest du es als Witz, aber wenn du Erlang willst, kannst du dir ja Elixir mit Phoenix angucken. Das wurde mir mal empfohlen und demonstriert und die Live View Technik sieht interessant aus, falls du auch den UI-Aspekt bedienen willst. Und es soll sehr gut skalierbar sein.

Wenn du doch irgendwie wieder zu Java zurück findest, kannst du eigentlich auch auf direkt auf Kotlin umsteigen. Ich habe aber von einem Bekannten gehört, dass man bei sehr großen Webanwendungen mit Spring und Co viel herumfummeln muss, um dort den Speicherverbrauch zu optimieren (sowohl Java als auch Kotlin).

Edit: Achja, Ruby kann man im Moment definitiv ausschließen. Performanz gehört eher zu den Schwächen von Ruby.
 
Zuletzt bearbeitet:
Wenn "Multi-Plattform, fürs Web geeignet, JS-ähnliche Syntax, Performance und starke Community" deine Anforderungen sind, ist die Antwort ziemlich eindeutig Rust oder Go.

Die anderen Sprachen sind entweder performant aber haben eine aufgeblähte, zeremonielle Syntax (Java, .NET), oder haben eine leichtgewichtige Syntax aber sind langsam (Python, Ruby). Dahingegen ist es ein erklärtes Ziel von Go, die Typsicherheit von stark typisierten Sprachen mit der einfachen Lesbarkeit von Skriptsprachen zu vereinen.

Ob dann Rust oder Go besser für dich geeignet ist, kommt darauf an, was du genau machen möchtest und wie du es machen möchtest. Beide Sprachen mag man entweder total gerne oder überhaupt nicht:
  • Rust legt sehr viel Wert auf technische Korrektheit und Effizienz und nimmt dafür auch eine höhere Komplexität der Sprache und der Libraries in Kauf. Rust ist eine Sprache, die immer 100% will, um das Optimum zu erreichen - und wird dich bis dahin auch nicht in Ruhe lassen. Du bekommst ein sehr performantes Programm, das mit hoher Wahrscheinlichkeit fehlerfrei funktioniert.
  • Go macht das Gegenteil und will dem Entwickler nicht im Weg stehen, sondern ihn auf die Businesslogik fokussieren lassen. Es gibt wenige Sprachkonstrukte und wenig Vorgaben. Go ist eine Sprache, der auch 95% reichen, wenn du dafür schneller und einfacher an dein Ziel kommst. Du bekommst ein performantes Programm, das einfach wartbar und einfach zu verstehen ist und "gut genug" funktioniert.
Ich bin beruflich Go-Entwickler, also offensichtlich voreingenommen. Für mich ist Go der bessere Trade-off, weil ich im echten Leben nicht (und schon gar nicht in der Firma für irgendwelche Web-Projekte) immer hundertprozentige Perfektion brauche, die mit viel Komplexität in der Sprache verbunden ist. Mir "reicht" Go. Teilweise ist es recht stumpf, weil es so abgespeckt ist - aber im Gegenzug kann ich Quellcode wie ein Buch lesen und die Businesslogik verstehen, ohne mir viel Gedanken um sprachliche Konstrukte machen zu müssen. Am Ende ist es Geschmackssache.
 
Zuletzt bearbeitet:
Heute auf Reddit gelesen:

Thinking of using Rust as a web backend?

Imagine that TypeScript is like a shitty but versatile truck. It will do all sorts of weird shit, including a godawful toolchain and bundling system, but it is well supported by the community and it will handle whatever you throw at it. A huge pothole in the road? It will somehow manage to come out the other end but it won't be pretty. It will get to the finish line slowly but surely.

Imagine Go as a nice a quick, but flawed sports car. It's got some really weird weird shit going on, but it's undeniably quick and well let you get to the finish line in comfort as long as you have the skill to stop it flipping the fuck over at the slightest touch. That's because it is not forgiving and will ruin your life if you're not ultra competent at software architecture concepts.

Now imagine Rust as a sexy fast formula 1 race car. It is geared to be as performant as possible, yet has the sleek, sexy feel that C and C++ lacks. It literally converts every electron coursing through your computer to gold. It's so good. It's so damn sexy.

Now imagine the Rust car at the starting line, revving its engines. People are cheering, so excited it will win. They're popping the champagne already.

Except the Rust car is not on a racetrack to the finish line. It's a racetrack up your asshole. The car goes in. Then out. Then in. Then out. At incredible speed. Blood is everywhere and you're basically crying at how amazingly fast it is, it's so damn cool. Yet your ass is getting ruined.

And that's why you don't develop your web backends on Rust unless you know exactly why the fuck you're doing it. There are very real, excellent use cases for Rust that make it the natural winner. But by god if you're using it cause you feel like a leet hacker and it's hype, weighting it as an equivalent choice to something like Ruby on Rails or nodejs, then my god you're screwed.


Dieser Kommentar trifft es wirklich zu 1000%.
 
Zurück
Oben Unten